Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement API endpoints #6915

2 changes: 1 addition & 1 deletion ododevapispec.yaml
Original file line number Diff line number Diff line change
@@ -188,7 +188,7 @@ paths:
enum:
- "push"
example:
action: push
name: push
responses:
'200':
description: command was successfully executed
1 change: 0 additions & 1 deletion pkg/apiserver-gen/README.md
Original file line number Diff line number Diff line change
@@ -14,7 +14,6 @@ To see how to make this your own, look here:

- API version: 0.1


### Running the server
To run the server, follow these simple steps:

87 changes: 56 additions & 31 deletions pkg/apiserver-impl/api_default_service.go
Original file line number Diff line number Diff line change
@@ -2,62 +2,87 @@ package apiserver_impl

import (
"context"
"errors"
openapi "github.com/redhat-developer/odo/pkg/apiserver-gen/go"
"fmt"
"net/http"

openapi "github.com/redhat-developer/odo/pkg/apiserver-gen/go"
"github.com/redhat-developer/odo/pkg/component/describe"
"github.com/redhat-developer/odo/pkg/kclient"
odocontext "github.com/redhat-developer/odo/pkg/odo/context"
"github.com/redhat-developer/odo/pkg/podman"
"github.com/redhat-developer/odo/pkg/state"
)

// DefaultApiService is a service that implements the logic for the DefaultApiServicer
// This service should implement the business logic for every endpoint for the DefaultApi API.
// Include any external packages or services that will be required by this service.
type DefaultApiService struct {
cancel context.CancelFunc
pushWatcher chan<- struct{}
kubeClient kclient.ClientInterface
podmanClient podman.Client
stateClient state.Client
}

// NewDefaultApiService creates a default api service
func NewDefaultApiService() openapi.DefaultApiServicer {
return &DefaultApiService{}
func NewDefaultApiService(
cancel context.CancelFunc,
pushWatcher chan<- struct{},
kubeClient kclient.ClientInterface,
podmanClient podman.Client,
stateClient state.Client,
) openapi.DefaultApiServicer {
return &DefaultApiService{
cancel: cancel,
pushWatcher: pushWatcher,
kubeClient: kubeClient,
podmanClient: podmanClient,
stateClient: stateClient,
}
}

// ComponentCommandPost -
func (s *DefaultApiService) ComponentCommandPost(ctx context.Context, componentCommandPostRequest openapi.ComponentCommandPostRequest) (openapi.ImplResponse, error) {
// TODO - update ComponentCommandPost with the required logic for this service method.
// Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation.
switch componentCommandPostRequest.Name {
case "push":
select {
case s.pushWatcher <- struct{}{}:
return openapi.Response(http.StatusOK, openapi.GeneralSuccess{
Message: "push was successfully executed",
}), nil
default:
return openapi.Response(http.StatusTooManyRequests, openapi.GeneralError{
Message: "a push operation is not possible at this time. Please retry later",
}), nil
}

// TODO: Uncomment the next line to return response Response(200, GeneralSuccess{}) or use other options such as http.Ok ...
// return Response(200, GeneralSuccess{}), nil

return openapi.Response(http.StatusNotImplemented, nil), errors.New("ComponentCommandPost method not implemented")
default:
return openapi.Response(http.StatusBadRequest, nil), fmt.Errorf("command name %q not supported. Supported values are: %q", componentCommandPostRequest.Name, "push")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if the response body in case of error should not be serialized as a real JSON string too. Currently, I get a simple string, which does not respect the response content type returned in the response headers:

$ http POST http://127.0.0.1:20002/api/v1/component/command name=fake-cmd
HTTP/1.1 400 Bad Request
Content-Length: 74
Content-Type: application/json; charset=UTF-8
Date: Thu, 22 Jun 2023 15:45:12 GMT

"command name \"fake-cmd\" not supported. Supported values are: \"push\""

I'd expect something like below. What do you think?

{
  "message": "command name \"fake-cmd\" not supported. Supported values are: \"push\""
}

Copy link
Contributor Author

@feloy feloy Jun 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we will need to complete the openapi spec, to define a return code in case of error, a,d to point to the correct GeneralError instead of using the default. WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that makes sense.

}
}

// ComponentGet -
func (s *DefaultApiService) ComponentGet(ctx context.Context) (openapi.ImplResponse, error) {
// TODO - update ComponentGet with the required logic for this service method.
// Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation.

// TODO: Uncomment the next line to return response Response(200, ComponentGet200Response{}) or use other options such as http.Ok ...
// return Response(200, ComponentGet200Response{}), nil

return openapi.Response(http.StatusNotImplemented, nil), errors.New("ComponentGet method not implemented")
value, _, err := describe.DescribeDevfileComponent(ctx, s.kubeClient, s.podmanClient, s.stateClient)
if err != nil {
return openapi.Response(http.StatusInternalServerError, ""), fmt.Errorf("error getting the description of the component: %w", err)
}
return openapi.Response(http.StatusOK, value), nil
}

// InstanceDelete -
func (s *DefaultApiService) InstanceDelete(ctx context.Context) (openapi.ImplResponse, error) {
// TODO - update InstanceDelete with the required logic for this service method.
// Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation.

// TODO: Uncomment the next line to return response Response(200, GeneralSuccess{}) or use other options such as http.Ok ...
// return Response(200, GeneralSuccess{}), nil

return openapi.Response(http.StatusNotImplemented, nil), errors.New("InstanceDelete method not implemented")
s.cancel()
return openapi.Response(http.StatusOK, openapi.GeneralSuccess{
Message: fmt.Sprintf("'odo dev' instance with pid: %d is shutting down.", odocontext.GetPID(ctx)),
}), nil
}

// InstanceGet -
func (s *DefaultApiService) InstanceGet(ctx context.Context) (openapi.ImplResponse, error) {
// TODO - update InstanceGet with the required logic for this service method.
// Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation.

// TODO: Uncomment the next line to return response Response(200, InstanceGet200Response{}) or use other options such as http.Ok ...
// return Response(200, InstanceGet200Response{}), nil

return openapi.Response(http.StatusNotImplemented, nil), errors.New("InstanceGet method not implemented")
response := openapi.InstanceGet200Response{
Pid: int32(odocontext.GetPID(ctx)),
ComponentDirectory: odocontext.GetWorkingDirectory(ctx),
}
return openapi.Response(http.StatusOK, response), nil
}
35 changes: 32 additions & 3 deletions pkg/apiserver-impl/starterserver.go
Original file line number Diff line number Diff line change
@@ -3,16 +3,38 @@ package apiserver_impl
import (
"context"
"fmt"
"net"
"net/http"

openapi "github.com/redhat-developer/odo/pkg/apiserver-gen/go"
"github.com/redhat-developer/odo/pkg/kclient"
"github.com/redhat-developer/odo/pkg/podman"
"github.com/redhat-developer/odo/pkg/state"
"github.com/redhat-developer/odo/pkg/util"
"k8s.io/klog"
"net/http"
)

func StartServer(ctx context.Context, cancelFunc context.CancelFunc, port int, stateClient state.Client) {
type ApiServer struct {
PushWatcher <-chan struct{}
}

func StartServer(
ctx context.Context,
cancelFunc context.CancelFunc,
port int,
kubernetesClient kclient.ClientInterface,
podmanClient podman.Client,
stateClient state.Client,
) ApiServer {

defaultApiService := NewDefaultApiService()
pushWatcher := make(chan struct{})
defaultApiService := NewDefaultApiService(
cancelFunc,
pushWatcher,
kubernetesClient,
podmanClient,
stateClient,
)
defaultApiController := openapi.NewDefaultApiController(defaultApiService)

router := openapi.NewRouter(defaultApiController)
@@ -38,6 +60,9 @@ func StartServer(ctx context.Context, cancelFunc context.CancelFunc, port int, s
server := &http.Server{Addr: fmt.Sprintf(":%d", port), Handler: router}
var errChan = make(chan error)
go func() {
server.BaseContext = func(net.Listener) context.Context {
return ctx
}
err = server.ListenAndServe()
errChan <- err
}()
@@ -54,4 +79,8 @@ func StartServer(ctx context.Context, cancelFunc context.CancelFunc, port int, s
cancelFunc()
}
}()

return ApiServer{
PushWatcher: pushWatcher,
}
}
Loading