diff --git a/client/activedocs.go b/client/activedocs.go new file mode 100644 index 0000000..5eb95a8 --- /dev/null +++ b/client/activedocs.go @@ -0,0 +1,162 @@ +package client + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" +) + +const ( + activeDocListEndpoint = "/admin/api/active_docs.json" + activeDocEndpoint = "/admin/api/active_docs/%d.json" +) + +// ListActiveDocs List existing activedocs for the client provider account +func (c *ThreeScaleClient) ListActiveDocs() (*ActiveDocList, error) { + req, err := c.buildGetReq(activeDocListEndpoint) + if err != nil { + return nil, err + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + activeDocList := &ActiveDocList{} + err = handleJsonResp(resp, http.StatusOK, activeDocList) + return activeDocList, err +} + +// ActiveDoc Reads 3scale Activedoc +func (c *ThreeScaleClient) ActiveDoc(id int64) (*ActiveDoc, error) { + endpoint := fmt.Sprintf(activeDocEndpoint, id) + + req, err := c.buildGetJSONReq(endpoint) + if err != nil { + return nil, err + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + activeDoc := &ActiveDoc{} + err = handleJsonResp(resp, http.StatusOK, activeDoc) + return activeDoc, err +} + +// CreateActiveDoc Create 3scale activedoc +func (c *ThreeScaleClient) CreateActiveDoc(activeDoc *ActiveDoc) (*ActiveDoc, error) { + bodyArr, err := json.Marshal(activeDoc.Element) + if err != nil { + return nil, err + } + body := bytes.NewReader(bodyArr) + + req, err := c.buildPostJSONReq(activeDocListEndpoint, body) + if err != nil { + return nil, err + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + respObj := &ActiveDoc{} + err = handleJsonResp(resp, http.StatusCreated, respObj) + return respObj, err +} + +// UpdateActiveDoc Update existing activedoc +func (c *ThreeScaleClient) UpdateActiveDoc(activeDoc *ActiveDoc) (*ActiveDoc, error) { + if activeDoc == nil { + return nil, errors.New("UpdateActiveDoc needs not nil pointer") + } + + if activeDoc.Element.ID == nil { + return nil, errors.New("UpdateActiveDoc needs not nil ID") + } + + endpoint := fmt.Sprintf(activeDocEndpoint, *activeDoc.Element.ID) + + bodyArr, err := json.Marshal(activeDoc.Element) + if err != nil { + return nil, err + } + body := bytes.NewReader(bodyArr) + + req, err := c.buildUpdateJSONReq(endpoint, body) + if err != nil { + return nil, err + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + respObj := &ActiveDoc{} + err = handleJsonResp(resp, http.StatusOK, respObj) + return respObj, err +} + +// DeleteActiveDoc Delete existing activedoc +func (c *ThreeScaleClient) DeleteActiveDoc(id int64) error { + endpoint := fmt.Sprintf(activeDocEndpoint, id) + + req, err := c.buildDeleteReq(endpoint, nil) + if err != nil { + return err + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + return handleJsonResp(resp, http.StatusOK, nil) +} + +// UnbindActiveDocFromProduct removes product relationship from activedoc object +func (c *ThreeScaleClient) UnbindActiveDocFromProduct(id int64) (*ActiveDoc, error) { + endpoint := fmt.Sprintf(activeDocEndpoint, id) + + data := struct { + ID int64 `json:"id"` + ServiceID *int64 `json:"service_id"` + }{ + id, + nil, + } + + bodyArr, err := json.Marshal(data) + if err != nil { + return nil, err + } + body := bytes.NewReader(bodyArr) + + req, err := c.buildUpdateJSONReq(endpoint, body) + if err != nil { + return nil, err + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + respObj := &ActiveDoc{} + err = handleJsonResp(resp, http.StatusOK, respObj) + return respObj, err +} diff --git a/client/activedocs_test.go b/client/activedocs_test.go new file mode 100644 index 0000000..5593ffa --- /dev/null +++ b/client/activedocs_test.go @@ -0,0 +1,336 @@ +package client + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "reflect" + "strings" + "testing" +) + +func TestListActiveDocs(t *testing.T) { + var ( + adID1 int64 = 1 + adID2 int64 = 2 + name1 = "ActiveDoc1" + name2 = "ActiveDoc2" + body = "{}" + endpoint = activeDocListEndpoint + list = ActiveDocList{ + ActiveDocs: []ActiveDoc{ + { + Element: ActiveDocItem{ + ID: &adID1, + Name: &name1, + Body: &body, + SystemName: &name1, + }, + }, + { + Element: ActiveDocItem{ + ID: &adID2, + Name: &name2, + Body: &body, + SystemName: &name2, + }, + }, + }, + } + ) + + httpClient := NewTestClient(func(req *http.Request) *http.Response { + if req.URL.Path != endpoint { + t.Fatalf("Path does not match. Expected [%s]; got [%s]", endpoint, req.URL.Path) + } + + if req.Method != http.MethodGet { + t.Fatalf("Method does not match. Expected [%s]; got [%s]", http.MethodGet, req.Method) + } + + responseBodyBytes, err := json.Marshal(list) + if err != nil { + t.Fatal(err) + } + + return &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewBuffer(responseBodyBytes)), + Header: make(http.Header), + } + }) + + credential := "someAccessToken" + c := NewThreeScale(NewTestAdminPortal(t), credential, httpClient) + resp, err := c.ListActiveDocs() + if err != nil { + t.Fatal(err) + } + + if resp == nil { + t.Fatal("response was nil") + } + + if !reflect.DeepEqual(*resp, list) { + got, _ := json.Marshal(*resp) + expected, _ := json.Marshal(list) + t.Logf("Expected %s; got %s", string(expected), string(got)) + t.Fatalf("Expected %v; got %v", expected, got) + } +} + +func TestReadActiveDocs(t *testing.T) { + var ( + adID1 int64 = 1 + name1 = "ActiveDoc1" + body = "{}" + endpoint = fmt.Sprintf(activeDocEndpoint, adID1) + activeDoc = ActiveDoc{ + Element: ActiveDocItem{ + ID: &adID1, + Name: &name1, + Body: &body, + SystemName: &name1, + }, + } + ) + + httpClient := NewTestClient(func(req *http.Request) *http.Response { + if req.URL.Path != endpoint { + t.Fatalf("Path does not match. Expected [%s]; got [%s]", endpoint, req.URL.Path) + } + + if req.Method != http.MethodGet { + t.Fatalf("Method does not match. Expected [%s]; got [%s]", http.MethodGet, req.Method) + } + + responseBodyBytes, err := json.Marshal(activeDoc) + if err != nil { + t.Fatal(err) + } + + return &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewBuffer(responseBodyBytes)), + Header: make(http.Header), + } + }) + + credential := "someAccessToken" + c := NewThreeScale(NewTestAdminPortal(t), credential, httpClient) + resp, err := c.ActiveDoc(adID1) + if err != nil { + t.Fatal(err) + } + + if resp == nil { + t.Fatal("response was nil") + } + + if !reflect.DeepEqual(*resp, activeDoc) { + got, _ := json.Marshal(*resp) + expected, _ := json.Marshal(activeDoc) + t.Logf("Expected %s; got %s", string(expected), string(got)) + t.Fatalf("Expected %v; got %v", expected, got) + } +} + +func TestCreateActiveDocs(t *testing.T) { + var ( + adID1 int64 = 1 + name1 = "ActiveDoc1" + body = "{}" + endpoint = activeDocListEndpoint + activeDoc = ActiveDoc{ + Element: ActiveDocItem{ + ID: &adID1, + Name: &name1, + Body: &body, + SystemName: &name1, + }, + } + ) + + httpClient := NewTestClient(func(req *http.Request) *http.Response { + if req.URL.Path != endpoint { + t.Fatalf("Path does not match. Expected [%s]; got [%s]", endpoint, req.URL.Path) + } + + if req.Method != http.MethodPost { + t.Fatalf("Method does not match. Expected [%s]; got [%s]", http.MethodPost, req.Method) + } + + responseBodyBytes, err := json.Marshal(activeDoc) + if err != nil { + t.Fatal(err) + } + + return &http.Response{ + StatusCode: http.StatusCreated, + Body: ioutil.NopCloser(bytes.NewBuffer(responseBodyBytes)), + Header: make(http.Header), + } + }) + + credential := "someAccessToken" + c := NewThreeScale(NewTestAdminPortal(t), credential, httpClient) + resp, err := c.CreateActiveDoc(&activeDoc) + if err != nil { + t.Fatal(err) + } + + if resp == nil { + t.Fatal("response was nil") + } + + if !reflect.DeepEqual(*resp, activeDoc) { + got, _ := json.Marshal(*resp) + expected, _ := json.Marshal(activeDoc) + t.Logf("Expected %s; got %s", string(expected), string(got)) + t.Fatalf("Expected %v; got %v", expected, got) + } +} + +func TestUpdateActiveDocs(t *testing.T) { + var ( + adID1 int64 = 1 + name1 = "ActiveDoc1" + body = "{}" + endpoint = fmt.Sprintf(activeDocEndpoint, adID1) + activeDoc = ActiveDoc{ + Element: ActiveDocItem{ + ID: &adID1, + Name: &name1, + Body: &body, + SystemName: &name1, + }, + } + ) + + httpClient := NewTestClient(func(req *http.Request) *http.Response { + if req.URL.Path != endpoint { + t.Fatalf("Path does not match. Expected [%s]; got [%s]", endpoint, req.URL.Path) + } + + if req.Method != http.MethodPut { + t.Fatalf("Method does not match. Expected [%s]; got [%s]", http.MethodPut, req.Method) + } + + responseBodyBytes, err := json.Marshal(activeDoc) + if err != nil { + t.Fatal(err) + } + + return &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewBuffer(responseBodyBytes)), + Header: make(http.Header), + } + }) + + credential := "someAccessToken" + c := NewThreeScale(NewTestAdminPortal(t), credential, httpClient) + resp, err := c.UpdateActiveDoc(&activeDoc) + if err != nil { + t.Fatal(err) + } + + if resp == nil { + t.Fatal("response was nil") + } + + if !reflect.DeepEqual(*resp, activeDoc) { + got, _ := json.Marshal(*resp) + expected, _ := json.Marshal(activeDoc) + t.Logf("Expected %s; got %s", string(expected), string(got)) + t.Fatalf("Expected %v; got %v", expected, got) + } +} + +func TestDeleteActiveDocs(t *testing.T) { + var ( + adID1 int64 = 1 + endpoint = fmt.Sprintf(activeDocEndpoint, adID1) + ) + + httpClient := NewTestClient(func(req *http.Request) *http.Response { + if req.URL.Path != endpoint { + t.Fatalf("Path does not match. Expected [%s]; got [%s]", endpoint, req.URL.Path) + } + + if req.Method != http.MethodDelete { + t.Fatalf("Method does not match. Expected [%s]; got [%s]", http.MethodDelete, req.Method) + } + + return &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(strings.NewReader("")), + Header: make(http.Header), + } + }) + + credential := "someAccessToken" + c := NewThreeScale(NewTestAdminPortal(t), credential, httpClient) + err := c.DeleteActiveDoc(adID1) + if err != nil { + t.Fatal(err) + } +} + +func TestUnbindActiveDocFromProduct(t *testing.T) { + var ( + adID1 int64 = 1 + name1 = "ActiveDoc1" + body = "{}" + endpoint = fmt.Sprintf(activeDocEndpoint, adID1) + activeDoc = ActiveDoc{ + Element: ActiveDocItem{ + ID: &adID1, + Name: &name1, + Body: &body, + SystemName: &name1, + }, + } + ) + + httpClient := NewTestClient(func(req *http.Request) *http.Response { + if req.URL.Path != endpoint { + t.Fatalf("Path does not match. Expected [%s]; got [%s]", endpoint, req.URL.Path) + } + + if req.Method != http.MethodPut { + t.Fatalf("Method does not match. Expected [%s]; got [%s]", http.MethodPut, req.Method) + } + + responseBodyBytes, err := json.Marshal(activeDoc) + if err != nil { + t.Fatal(err) + } + + return &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewBuffer(responseBodyBytes)), + Header: make(http.Header), + } + }) + + credential := "someAccessToken" + c := NewThreeScale(NewTestAdminPortal(t), credential, httpClient) + resp, err := c.UnbindActiveDocFromProduct(adID1) + if err != nil { + t.Fatal(err) + } + + if resp == nil { + t.Fatal("response was nil") + } + + if !reflect.DeepEqual(*resp, activeDoc) { + got, _ := json.Marshal(*resp) + expected, _ := json.Marshal(activeDoc) + t.Logf("Expected %s; got %s", string(expected), string(got)) + t.Fatalf("Expected %v; got %v", expected, got) + } +} diff --git a/client/client.go b/client/client.go index 44b205e..b8fb75b 100644 --- a/client/client.go +++ b/client/client.go @@ -114,6 +114,15 @@ func (c *ThreeScaleClient) buildPostReq(ep string, body io.Reader) (*http.Reques return req, err } +// Request builder for POST request to the provided endpoint +func (c *ThreeScaleClient) buildPostJSONReq(ep string, body io.Reader) (*http.Request, error) { + req, err := http.NewRequest("POST", c.adminPortal.rawURL+ep, body) + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Basic "+basicAuth("", c.credential)) + return req, err +} + // Request builder for PUT request to the provided endpoint func (c *ThreeScaleClient) buildUpdateReq(ep string, body io.Reader) (*http.Request, error) { req, err := http.NewRequest("PUT", c.adminPortal.rawURL+ep, body) diff --git a/client/types.go b/client/types.go index 5cbb282..a200639 100644 --- a/client/types.go +++ b/client/types.go @@ -649,3 +649,24 @@ type OIDCConfigurationItem struct { type OIDCConfiguration struct { Element OIDCConfigurationItem `json:"oidc_configuration"` } + +type ActiveDocItem struct { + ID *int64 `json:"id,omitempty"` + SystemName *string `json:"system_name,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Published *bool `json:"published,omitempty"` + SkipSwaggerValidations *bool `json:"skip_swagger_validations,omitempty"` + Body *string `json:"body,omitempty"` + ServiceID *int64 `json:"service_id,omitempty"` + CreatedAt *string `json:"created_at,omitempty"` + UpdatedAt *string `json:"updated_at,omitempty"` +} + +type ActiveDoc struct { + Element ActiveDocItem `json:"api_doc"` +} + +type ActiveDocList struct { + ActiveDocs []ActiveDoc `json:"api_docs"` +}