Skip to content

Commit b919acc

Browse files
feat: add services list command
Signed-off-by: puneeth_aditya_5656 <myakampuneeth@gmail.com>
1 parent c01f511 commit b919acc

5 files changed

Lines changed: 323 additions & 0 deletions

File tree

cmd/cmd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ func NewCommad() *cobra.Command {
4848
command.AddCommand(NewContextCommand(&clientOpts))
4949
command.AddCommand(NewLoginCommand(&clientOpts))
5050
command.AddCommand(NewLogoutCommand(&clientOpts))
51+
command.AddCommand(NewServicesCommand(&clientOpts))
5152

5253
defaultLocalConfigPath, err := config.DefaultLocalConfigPath()
5354
errors.CheckError(err)

cmd/services.go

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* Copyright The Microcks Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package cmd
17+
18+
import (
19+
"fmt"
20+
"os"
21+
"strings"
22+
"text/tabwriter"
23+
24+
"github.com/microcks/microcks-cli/pkg/config"
25+
"github.com/microcks/microcks-cli/pkg/connectors"
26+
"github.com/microcks/microcks-cli/pkg/errors"
27+
"github.com/spf13/cobra"
28+
)
29+
30+
func NewServicesCommand(globalClientOpts *connectors.ClientOptions) *cobra.Command {
31+
servicesCmd := &cobra.Command{
32+
Use: "services",
33+
Short: "Manage Microcks services",
34+
Long: `Manage Microcks services`,
35+
Run: func(cmd *cobra.Command, args []string) {
36+
cmd.HelpFunc()(cmd, args)
37+
},
38+
}
39+
40+
servicesCmd.AddCommand(newServicesListCommand(globalClientOpts))
41+
return servicesCmd
42+
}
43+
44+
func newServicesListCommand(globalClientOpts *connectors.ClientOptions) *cobra.Command {
45+
var (
46+
page int
47+
size int
48+
)
49+
50+
listCmd := &cobra.Command{
51+
Use: "list",
52+
Short: "List services imported in Microcks",
53+
Long: `List services imported in Microcks`,
54+
Example: `# List services using current context
55+
microcks services list
56+
57+
# List services with pagination
58+
microcks services list --page 1 --size 10`,
59+
Run: func(cmd *cobra.Command, args []string) {
60+
config.InsecureTLS = globalClientOpts.InsecureTLS
61+
config.CaCertPaths = globalClientOpts.CaCertPaths
62+
config.Verbose = globalClientOpts.Verbose
63+
64+
var mc connectors.MicrocksClient
65+
66+
if globalClientOpts.ServerAddr != "" && globalClientOpts.ClientId != "" && globalClientOpts.ClientSecret != "" {
67+
mc = connectors.NewMicrocksClient(globalClientOpts.ServerAddr)
68+
69+
keycloakURL, err := mc.GetKeycloakURL()
70+
if err != nil {
71+
fmt.Printf("Got error when invoking Microcks client retrieving config: %s", err)
72+
os.Exit(1)
73+
}
74+
75+
var oauthToken string = "unauthenticated-token"
76+
if keycloakURL != "null" {
77+
kc := connectors.NewKeycloakClient(keycloakURL, globalClientOpts.ClientId, globalClientOpts.ClientSecret)
78+
oauthToken, err = kc.ConnectAndGetToken()
79+
if err != nil {
80+
fmt.Printf("Got error when invoking Keycloak client: %s", err)
81+
os.Exit(1)
82+
}
83+
}
84+
mc.SetOAuthToken(oauthToken)
85+
} else {
86+
localConfig, err := config.ReadLocalConfig(globalClientOpts.ConfigPath)
87+
if err != nil {
88+
fmt.Println(err)
89+
return
90+
}
91+
92+
if localConfig == nil {
93+
fmt.Println("Please login to perform operation...")
94+
return
95+
}
96+
97+
if globalClientOpts.Context == "" {
98+
globalClientOpts.Context = localConfig.CurrentContext
99+
}
100+
101+
mc, err = connectors.NewClient(*globalClientOpts)
102+
if err != nil {
103+
fmt.Printf("error %v", err)
104+
return
105+
}
106+
}
107+
108+
services, err := mc.GetServices(page, size)
109+
if err != nil {
110+
fmt.Printf("Got error when listing services: %s", err)
111+
os.Exit(1)
112+
}
113+
114+
if len(services) == 0 {
115+
fmt.Println("No services found")
116+
return
117+
}
118+
119+
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
120+
defer func() { _ = w.Flush() }()
121+
columnNames := []string{"NAME", "VERSION", "TYPE"}
122+
_, err = fmt.Fprintf(w, "%s\n", strings.Join(columnNames, "\t"))
123+
errors.CheckError(err)
124+
125+
for _, svc := range services {
126+
_, err = fmt.Fprintf(w, "%s\t%s\t%s\n", svc.Name, svc.Version, svc.Type)
127+
errors.CheckError(err)
128+
}
129+
},
130+
}
131+
132+
listCmd.Flags().IntVar(&page, "page", 0, "Page of services to retrieve (0-indexed)")
133+
listCmd.Flags().IntVar(&size, "size", 20, "Number of services per page")
134+
return listCmd
135+
}

documentation/cmd/services.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
## `microcks services list` – List Services Imported in Microcks
2+
3+
Lists the services (APIs and mocks) currently imported in the connected Microcks instance.
4+
5+
### Usage
6+
```bash
7+
microcks services list [flags]
8+
```
9+
10+
### Flags
11+
| Flag | Default | Description |
12+
| -------- | ------- | -------------------------------------- |
13+
| `--page` | `0` | Page of services to retrieve (0-indexed) |
14+
| `--size` | `20` | Number of services per page |
15+
16+
### Examples
17+
```bash
18+
# List services using the current context
19+
microcks services list
20+
21+
# List the second page of results with 10 services per page
22+
microcks services list --page 1 --size 10
23+
24+
# List services against a specific Microcks instance
25+
microcks services list --microcksURL http://localhost:8585 \
26+
--keycloakClientId my-client \
27+
--keycloakClientSecret my-secret
28+
```
29+
30+
### Options Inherited from Parent Commands
31+
| Flag | Description |
32+
| ------------------------ | ------------------------------------------- |
33+
| `--config` | Path to Microcks config file |
34+
| `--microcks-context` | Name of the Microcks context to use |
35+
| `--verbose` | Produce dumps of HTTP exchanges |
36+
| `--insecure-tls` | Allow insecure HTTPS connections |
37+
| `--caCerts` | Comma-separated paths of CA cert files |
38+
| `--keycloakClientId` | Keycloak Realm Service Account ClientId |
39+
| `--keycloakClientSecret` | Keycloak Realm Service Account ClientSecret |
40+
| `--microcksURL` | Microcks API URL |

pkg/connectors/microcks_client.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ type MicrocksClient interface {
5252
GetTestResult(testResultID string) (*TestResultSummary, error)
5353
UploadArtifact(specificationFilePath string, mainArtifact bool) (string, error)
5454
DownloadArtifact(artifactURL string, mainArtifact bool, secret string) (string, error)
55+
GetServices(page, size int) ([]ServiceSummary, error)
5556
}
5657

5758
// TestResultSummary represents a simple view on Microcks TestResult
@@ -67,6 +68,14 @@ type TestResultSummary struct {
6768
InProgress bool `json:"inProgress"`
6869
}
6970

71+
// ServiceSummary represents a simple view on a Microcks Service
72+
type ServiceSummary struct {
73+
ID string `json:"id"`
74+
Name string `json:"name"`
75+
Version string `json:"version"`
76+
Type string `json:"type"`
77+
}
78+
7079
// HeaderDTO represents an operation header passed for Test
7180
type HeaderDTO struct {
7281
Name string `json:"name"`
@@ -535,6 +544,46 @@ func (c *microcksClient) DownloadArtifact(artifactURL string, mainArtifact bool,
535544
return string(respBody), err
536545
}
537546

547+
func (c *microcksClient) GetServices(page, size int) ([]ServiceSummary, error) {
548+
rel := &url.URL{
549+
Path: "services",
550+
RawQuery: fmt.Sprintf("page=%d&size=%d", page, size),
551+
}
552+
u := c.APIURL.ResolveReference(rel)
553+
554+
req, err := http.NewRequest("GET", u.String(), nil)
555+
if err != nil {
556+
return nil, err
557+
}
558+
559+
req.Header.Set("Accept", "application/json")
560+
req.Header.Set("Authorization", "Bearer "+c.AuthToken)
561+
562+
// Dump request if verbose required.
563+
config.DumpRequestIfRequired("Microcks for listing services", req, false)
564+
565+
resp, err := c.httpClient.Do(req)
566+
if err != nil {
567+
return nil, err
568+
}
569+
defer resp.Body.Close()
570+
571+
// Dump response if verbose required.
572+
config.DumpResponseIfRequired("Microcks for listing services", resp, true)
573+
574+
body, err := io.ReadAll(resp.Body)
575+
if err != nil {
576+
panic(err.Error())
577+
}
578+
579+
var services []ServiceSummary
580+
if err := json.Unmarshal(body, &services); err != nil {
581+
return nil, fmt.Errorf("failed to parse services response: %w", err)
582+
}
583+
584+
return services, nil
585+
}
586+
538587
func ensureValidOperationsList(filteredOperations string) bool {
539588
// Unmarshal using a generic interface
540589
var list = []string{}

pkg/connectors/microcks_client_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package connectors
22

33
import (
4+
"encoding/json"
45
"net/http"
56
"net/http/httptest"
67
"strings"
@@ -41,3 +42,100 @@ func TestDownloadArtifactReturnsResponseBody(t *testing.T) {
4142
t.Fatalf("expected response body %q, got %q", expectedBody, msg)
4243
}
4344
}
45+
46+
func TestGetServices(t *testing.T) {
47+
services := []ServiceSummary{
48+
{ID: "1", Name: "Petstore API", Version: "1.0", Type: "REST"},
49+
{ID: "2", Name: "HelloService", Version: "0.9", Type: "SOAP"},
50+
}
51+
52+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
53+
if r.URL.Path != "/api/services" {
54+
t.Fatalf("unexpected path: %s", r.URL.Path)
55+
}
56+
if r.Method != http.MethodGet {
57+
t.Fatalf("unexpected method: %s", r.Method)
58+
}
59+
if got := r.URL.Query().Get("page"); got != "0" {
60+
t.Fatalf("unexpected page: %s", got)
61+
}
62+
if got := r.URL.Query().Get("size"); got != "20" {
63+
t.Fatalf("unexpected size: %s", got)
64+
}
65+
w.Header().Set("Content-Type", "application/json")
66+
_ = json.NewEncoder(w).Encode(services)
67+
}))
68+
defer server.Close()
69+
70+
client := NewMicrocksClient(server.URL)
71+
result, err := client.GetServices(0, 20)
72+
if err != nil {
73+
t.Fatalf("GetServices returned error: %v", err)
74+
}
75+
if len(result) != 2 {
76+
t.Fatalf("expected 2 services, got %d", len(result))
77+
}
78+
if result[0].Name != "Petstore API" {
79+
t.Fatalf("expected first service name %q, got %q", "Petstore API", result[0].Name)
80+
}
81+
if result[0].Version != "1.0" {
82+
t.Fatalf("expected first service version %q, got %q", "1.0", result[0].Version)
83+
}
84+
if result[0].Type != "REST" {
85+
t.Fatalf("expected first service type %q, got %q", "REST", result[0].Type)
86+
}
87+
}
88+
89+
func TestGetServicesEmpty(t *testing.T) {
90+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
91+
w.Header().Set("Content-Type", "application/json")
92+
_, _ = w.Write([]byte("[]"))
93+
}))
94+
defer server.Close()
95+
96+
client := NewMicrocksClient(server.URL)
97+
result, err := client.GetServices(0, 20)
98+
if err != nil {
99+
t.Fatalf("GetServices returned error: %v", err)
100+
}
101+
if len(result) != 0 {
102+
t.Fatalf("expected empty slice, got %d services", len(result))
103+
}
104+
}
105+
106+
func TestGetServicesInvalidJSON(t *testing.T) {
107+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
108+
w.Header().Set("Content-Type", "application/json")
109+
_, _ = w.Write([]byte("not-json"))
110+
}))
111+
defer server.Close()
112+
113+
client := NewMicrocksClient(server.URL)
114+
_, err := client.GetServices(0, 20)
115+
if err == nil {
116+
t.Fatal("expected error for invalid JSON, got nil")
117+
}
118+
if !strings.Contains(err.Error(), "failed to parse services response") {
119+
t.Fatalf("unexpected error message: %v", err)
120+
}
121+
}
122+
123+
func TestGetServicesPagination(t *testing.T) {
124+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
125+
if got := r.URL.Query().Get("page"); got != "2" {
126+
t.Fatalf("unexpected page: %s", got)
127+
}
128+
if got := r.URL.Query().Get("size"); got != "5" {
129+
t.Fatalf("unexpected size: %s", got)
130+
}
131+
w.Header().Set("Content-Type", "application/json")
132+
_, _ = w.Write([]byte("[]"))
133+
}))
134+
defer server.Close()
135+
136+
client := NewMicrocksClient(server.URL)
137+
_, err := client.GetServices(2, 5)
138+
if err != nil {
139+
t.Fatalf("GetServices returned error: %v", err)
140+
}
141+
}

0 commit comments

Comments
 (0)