From fd3c4378acd623ab1e4e3e1705c69cb3857a56d5 Mon Sep 17 00:00:00 2001 From: David White Date: Mon, 4 Dec 2023 14:00:05 -0700 Subject: [PATCH] Feat: Add Logout API Call Adds the /logout call to the openAPI generation code to gracefully end an API session --- api/mc-openapi.yaml | 14 ++++ internal/generator/cmd/main.go | 2 +- internal/generator/spec.go | 19 +++++- pkg/api/mcapi.go | 9 +++ pkg/api/volumes.go | 6 +- pkg/client/README.md | 1 + pkg/client/api/openapi.yaml | 13 ++++ pkg/client/api_default.go | 100 ++++++++++++++++++++++++++++ pkg/client/docs/DefaultApi.md | 62 +++++++++++++++++ pkg/client/test/api_default_test.go | 31 ++++++--- pkg/regression/v2_login.go | 12 ++++ 11 files changed, 254 insertions(+), 15 deletions(-) diff --git a/api/mc-openapi.yaml b/api/mc-openapi.yaml index 0d6733f..18f09c9 100644 --- a/api/mc-openapi.yaml +++ b/api/mc-openapi.yaml @@ -28,6 +28,20 @@ paths: application/json: schema: $ref: '#/components/schemas/statusObject' + # Logout + /logout: + get: + description: Log out of the storage array management controller + operationId: LogoutGet + security: + - basicAuth: [] + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/statusObject' # Meta /meta/{schemaId}: get: diff --git a/internal/generator/cmd/main.go b/internal/generator/cmd/main.go index 91183eb..d58d067 100644 --- a/internal/generator/cmd/main.go +++ b/internal/generator/cmd/main.go @@ -57,7 +57,7 @@ func main() { s := &generator.Specification{} s.Reset() s.GenerateHeader(ctx) - s.GenerateLogin(ctx) + s.GenerateLoginLogout(ctx) s.GenerateMetaSchema(ctx) logger.V(0).Info(">> generate openapi spec...", "commands", len(yamlc.Commands), "exceptions", len(yamlc.Exceptions)) diff --git a/internal/generator/spec.go b/internal/generator/spec.go index 961faf3..182c453 100644 --- a/internal/generator/spec.go +++ b/internal/generator/spec.go @@ -179,8 +179,8 @@ func (s *Specification) GenerateHeader(ctx context.Context) error { return nil } -// GenerateLogin: Add all required lines for MC login path -func (s *Specification) GenerateLogin(ctx context.Context) error { +// GenerateLoginLogout: Add all required lines for MC login and logout paths +func (s *Specification) GenerateLoginLogout(ctx context.Context) error { s.Paths(1, "# Login") s.Paths(1, "/login:") @@ -197,6 +197,21 @@ func (s *Specification) GenerateLogin(ctx context.Context) error { s.Paths(7, "schema:") s.Paths(8, "$ref: '#/components/schemas/statusObject'") + s.Paths(1, "# Logout") + s.Paths(1, "/logout:") + s.Paths(2, "get:") + s.Paths(3, "description: Log out of the storage array management controller") + s.Paths(3, "operationId: LogoutGet") + s.Paths(3, "security:") + s.Paths(3, "- basicAuth: []") + s.Paths(3, "responses:") + s.Paths(4, "'200':") + s.Paths(5, "description: OK") + s.Paths(5, "content:") + s.Paths(6, "application/json:") + s.Paths(7, "schema:") + s.Paths(8, "$ref: '#/components/schemas/statusObject'") + s.generatedResources["status"] = true s.Resources(2, "statusResource:") s.Resources(3, "type: array") diff --git a/pkg/api/mcapi.go b/pkg/api/mcapi.go index d8ba8ff..b8a0bbd 100644 --- a/pkg/api/mcapi.go +++ b/pkg/api/mcapi.go @@ -81,6 +81,15 @@ func (client *Client) Login(ctx context.Context) error { return err } +// Login: Called to log into the storage controller API +func (client *Client) Logout() error { + if client.Ctx == nil { + client.Ctx = context.Background() + } + _, _, err := client.apiClient.DefaultApi.LogoutGet(client.Ctx).Execute() + return err +} + // SessionValid : Determine if a session is valid, if not a login is required func (client *Client) SessionValid(addr, username string) bool { if client.Ctx == nil { diff --git a/pkg/api/volumes.go b/pkg/api/volumes.go index 6dbc898..bc7059a 100644 --- a/pkg/api/volumes.go +++ b/pkg/api/volumes.go @@ -147,12 +147,16 @@ func (client *Client) ShowVolumes(volume string) ([]common.VolumeObject, *common } } else { logger.V(4).Info("-- ShowVolumesNamesGet", "status", httpRes.Status, "err", err, "body", httpRes.Body) + return nil, &common.ResponseStatus{}, err } logger.V(4).Info("================================================================================") returnVolumes := []common.VolumeObject{} - status := CreateCommonStatus(logger, &response.Status) + status := &common.ResponseStatus{} + if response.Status != nil { + status = CreateCommonStatus(logger, &response.Status) + } if response != nil { for _, v := range response.Volumes { diff --git a/pkg/client/README.md b/pkg/client/README.md index 704209b..cde568d 100644 --- a/pkg/client/README.md +++ b/pkg/client/README.md @@ -87,6 +87,7 @@ Class | Method | HTTP request | Description *DefaultApi* | [**DeleteVolumesNamesGet**](docs/DefaultApi.md#deletevolumesnamesget) | **Get** /delete/volumes/{namesOption} | *DefaultApi* | [**ExpandVolumeSizeNameGet**](docs/DefaultApi.md#expandvolumesizenameget) | **Get** /expand/volume/size/{sizeOption}/{nameOption} | *DefaultApi* | [**LoginGet**](docs/DefaultApi.md#loginget) | **Get** /login | +*DefaultApi* | [**LogoutGet**](docs/DefaultApi.md#logoutget) | **Get** /logout | *DefaultApi* | [**MapVolumeAccessLunInitiatorNamesGet**](docs/DefaultApi.md#mapvolumeaccessluninitiatornamesget) | **Get** /map/volume/access/{accessOption}/lun/{lunOption}/initiator/{initiatorOption}/{namesOption} | *DefaultApi* | [**SchemaGet**](docs/DefaultApi.md#schemaget) | **Get** /meta/{schemaId} | *DefaultApi* | [**SetInitiatorIdNicknameGet**](docs/DefaultApi.md#setinitiatoridnicknameget) | **Get** /set/initiator/id/{idOption}/nickname/{nicknameOption} | diff --git a/pkg/client/api/openapi.yaml b/pkg/client/api/openapi.yaml index 06bd73f..1c3ff3e 100644 --- a/pkg/client/api/openapi.yaml +++ b/pkg/client/api/openapi.yaml @@ -26,6 +26,19 @@ paths: description: OK security: - basicAuth: [] + /logout: + get: + description: Log out of the storage array management controller + operationId: LogoutGet + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/statusObject' + description: OK + security: + - basicAuth: [] /meta/{schemaId}: get: description: Retrieve schema definition for a resource diff --git a/pkg/client/api_default.go b/pkg/client/api_default.go index 497fa38..202318c 100644 --- a/pkg/client/api_default.go +++ b/pkg/client/api_default.go @@ -1062,6 +1062,106 @@ func (a *DefaultApiService) LoginGetExecute(r ApiLoginGetRequest) (*StatusObject return localVarReturnValue, localVarHTTPResponse, nil } +type ApiLogoutGetRequest struct { + ctx context.Context + ApiService *DefaultApiService +} + +func (r ApiLogoutGetRequest) Execute() (*StatusObject, *http.Response, error) { + return r.ApiService.LogoutGetExecute(r) +} + +/* +LogoutGet Method for LogoutGet + +Log out of the storage array management controller + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiLogoutGetRequest +*/ +func (a *DefaultApiService) LogoutGet(ctx context.Context) ApiLogoutGetRequest { + return ApiLogoutGetRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// +// @return StatusObject +func (a *DefaultApiService) LogoutGetExecute(r ApiLogoutGetRequest) (*StatusObject, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *StatusObject + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "DefaultApiService.LogoutGet") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/logout" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + type ApiMapVolumeAccessLunInitiatorNamesGetRequest struct { ctx context.Context ApiService *DefaultApiService diff --git a/pkg/client/docs/DefaultApi.md b/pkg/client/docs/DefaultApi.md index 1f84b51..2580a63 100644 --- a/pkg/client/docs/DefaultApi.md +++ b/pkg/client/docs/DefaultApi.md @@ -13,6 +13,7 @@ Method | HTTP request | Description [**DeleteVolumesNamesGet**](DefaultApi.md#DeleteVolumesNamesGet) | **Get** /delete/volumes/{namesOption} | [**ExpandVolumeSizeNameGet**](DefaultApi.md#ExpandVolumeSizeNameGet) | **Get** /expand/volume/size/{sizeOption}/{nameOption} | [**LoginGet**](DefaultApi.md#LoginGet) | **Get** /login | +[**LogoutGet**](DefaultApi.md#LogoutGet) | **Get** /logout | [**MapVolumeAccessLunInitiatorNamesGet**](DefaultApi.md#MapVolumeAccessLunInitiatorNamesGet) | **Get** /map/volume/access/{accessOption}/lun/{lunOption}/initiator/{initiatorOption}/{namesOption} | [**SchemaGet**](DefaultApi.md#SchemaGet) | **Get** /meta/{schemaId} | [**SetInitiatorIdNicknameGet**](DefaultApi.md#SetInitiatorIdNicknameGet) | **Get** /set/initiator/id/{idOption}/nickname/{nicknameOption} | @@ -687,6 +688,67 @@ Other parameters are passed through a pointer to a apiLoginGetRequest struct via [[Back to README]](../README.md) +## LogoutGet + +> StatusObject LogoutGet(ctx).Execute() + + + + + +### Example + +```go +package main + +import ( + "context" + "fmt" + "os" + openapiclient "github.com/GIT_USER_ID/GIT_REPO_ID" +) + +func main() { + + configuration := openapiclient.NewConfiguration() + apiClient := openapiclient.NewAPIClient(configuration) + resp, r, err := apiClient.DefaultApi.LogoutGet(context.Background()).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `DefaultApi.LogoutGet``: %v\n", err) + fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) + } + // response from `LogoutGet`: StatusObject + fmt.Fprintf(os.Stdout, "Response from `DefaultApi.LogoutGet`: %v\n", resp) +} +``` + +### Path Parameters + +This endpoint does not need any parameter. + +### Other Parameters + +Other parameters are passed through a pointer to a apiLogoutGetRequest struct via the builder pattern + + +### Return type + +[**StatusObject**](StatusObject.md) + +### Authorization + +[basicAuth](../README.md#basicAuth) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) +[[Back to Model list]](../README.md#documentation-for-models) +[[Back to README]](../README.md) + + ## MapVolumeAccessLunInitiatorNamesGet > StatusObject MapVolumeAccessLunInitiatorNamesGet(ctx, accessOption, lunOption, initiatorOption, namesOption).Execute() diff --git a/pkg/client/test/api_default_test.go b/pkg/client/test/api_default_test.go index 8bf6713..1678e2b 100644 --- a/pkg/client/test/api_default_test.go +++ b/pkg/client/test/api_default_test.go @@ -11,11 +11,10 @@ package client import ( "context" - "testing" - - openapiclient "github.com/Seagate/seagate-exos-x-api-go/v2/pkg/client" + openapiclient "github.com/GIT_USER_ID/GIT_REPO_ID" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "testing" ) func Test_client_DefaultApiService(t *testing.T) { @@ -142,19 +141,29 @@ func Test_client_DefaultApiService(t *testing.T) { }) - // t.Run("Test DefaultApiService LoginGetByHash", func(t *testing.T) { + t.Run("Test DefaultApiService LoginGet", func(t *testing.T) { - // t.Skip("skip test") // remove to run test + t.Skip("skip test") // remove to run test - // var loginHash string + resp, httpRes, err := apiClient.DefaultApi.LoginGet(context.Background()).Execute() - // resp, httpRes, err := apiClient.DefaultApi.LoginGetByHash(context.Background(), loginHash).Execute() + require.Nil(t, err) + require.NotNil(t, resp) + assert.Equal(t, 200, httpRes.StatusCode) + + }) - // require.Nil(t, err) - // require.NotNil(t, resp) - // assert.Equal(t, 200, httpRes.StatusCode) + t.Run("Test DefaultApiService LogoutGet", func(t *testing.T) { - // }) + t.Skip("skip test") // remove to run test + + resp, httpRes, err := apiClient.DefaultApi.LogoutGet(context.Background()).Execute() + + require.Nil(t, err) + require.NotNil(t, resp) + assert.Equal(t, 200, httpRes.StatusCode) + + }) t.Run("Test DefaultApiService MapVolumeAccessLunInitiatorNamesGet", func(t *testing.T) { diff --git a/pkg/regression/v2_login.go b/pkg/regression/v2_login.go index 3c5673f..0575e36 100644 --- a/pkg/regression/v2_login.go +++ b/pkg/regression/v2_login.go @@ -32,5 +32,17 @@ var _ = DescribeRegression("Login Testing (v2)", func(tc *TestContext) { Expect(valid).To(Equal(true)) }) + It("should be able to log out of the storage controller", func() { + err := client.Logout() + logger := klog.FromContext(tc.Config.Ctx) + logger.V(3).Info("Login", "ip", client.Addr, "username", client.Username, "err", err) + Expect(err).To(BeNil()) + }) + + It("should be an invalid session", func() { + valid := client.SessionValid(client.Addr, client.Username) + Expect(valid).To(Equal(false)) + }) + }) })