Repository: VictorAvelar/mollie-api-go
Pull request #117 (2021-02-03 22:59:34)

Add permissions API resource
<!--- Provide a general summary of your changes in the Title above -->

## Description

Added the Permissions API resources.

## Motivation and context

To offer 100% API parity

## Types of changes

What types of changes does your code introduce? Put an `x` in all the boxes that apply:
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)

## Checklist:

Go over all the following points, and put an `x` in all the boxes that apply.

Please, please, please, don't send your pull request until all of the boxes are ticked. Once your pull request is created, it will trigger a build on our continuous integration server to make sure your [tests and code style pass](https://help.github.com/articles/about-required-status-checks/).

- [x] I have read the **[CONTRIBUTING](CONTRIBUTING.md)** document.
- [x] My pull request addresses exactly one patch/feature.
- [x] I have created a branch for this patch/feature.
- [x] Each individual commit in the pull request is meaningful.
- [x] I have added tests to cover my changes.
- [x] If my change requires a change to the documentation, I have updated it accordingly.



Patch:
diff --git a/mollie/mollie.go b/mollie/mollie.go
index c6486c3..4b869dc 100644
--- a/mollie/mollie.go
+++ b/mollie/mollie.go
@@ -53,6 +53,7 @@ type Client struct {
 	Customers     *CustomersService
 	Miscellaneous *MiscellaneousService
 	Mandates      *MandatesService
+	Permissions   *PermissionsService
 }
 
 type service struct {
@@ -174,6 +175,7 @@ func NewClient(baseClient *http.Client, c *Config) (mollie *Client, err error) {
 	mollie.Customers = (*CustomersService)(&mollie.common)
 	mollie.Miscellaneous = (*MiscellaneousService)(&mollie.common)
 	mollie.Mandates = (*MandatesService)(&mollie.common)
+	mollie.Permissions = (*PermissionsService)(&mollie.common)
 
 	// Parse authorization from specified environment variable
 	tkn, ok := os.LookupEnv(c.auth)
diff --git a/mollie/permissions.go b/mollie/permissions.go
new file mode 100644
index 0000000..5d27627
--- /dev/null
+++ b/mollie/permissions.go
@@ -0,0 +1,103 @@
+package mollie
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+)
+
+// PermissionGrant defines supported permissions.
+type PermissionGrant string
+
+// Available permission grants.
+const (
+	PaymentsRead       PermissionGrant = "payments.read"
+	PaymentsWrite      PermissionGrant = "payments.write"
+	RefundsRead        PermissionGrant = "refunds.read"
+	RefundsWrite       PermissionGrant = "refunds.write"
+	CustomersRead      PermissionGrant = "customers.read"
+	CustomersWrite     PermissionGrant = "customers.write"
+	MandatesRead       PermissionGrant = "mandates.read"
+	MandatesWrite      PermissionGrant = "mandates.write"
+	SubscriptionsRead  PermissionGrant = "subscriptions.read"
+	SubscriptionsWrite PermissionGrant = "subscriptions.write"
+	ProfilesRead       PermissionGrant = "profiles.read"
+	ProfilesWrite      PermissionGrant = "profiles.write"
+	InvoicesRead       PermissionGrant = "invoices.read"
+	OrdersRead         PermissionGrant = "orders.read"
+	OrdersWrite        PermissionGrant = "orders.write"
+	ShipmentsRead      PermissionGrant = "shipments.read"
+	ShipmentsWrite     PermissionGrant = "shipments.write"
+	OrganizationsRead  PermissionGrant = "organizations.read"
+	OrganizationsWrite PermissionGrant = "organizations.write"
+	OnboardingRead     PermissionGrant = "onbording.read"
+	OnboardingWrite    PermissionGrant = "onbording.write"
+)
+
+// Permission represents an action that
+// can be performed by any API actor.
+type Permission struct {
+	Resource    string          `json:"resource,omitempty"`
+	ID          PermissionGrant `json:"id,omitempty"`
+	Description string          `json:"description,omitempty"`
+	Granted     bool            `json:"granted,omitempty"`
+	Links       PermissionLinks `json:"_links,omitempty"`
+}
+
+// PermissionLinks contains URL objects that make
+// reference to an http addres related to permissions.
+type PermissionLinks struct {
+	Self          *URL `json:"self,omitempty"`
+	Documentation *URL `json:"documentation,omitempty"`
+}
+
+// PermissionsList lists all the permissions given to an
+// API actor.
+type PermissionsList struct {
+	Count    int `json:"count,omitempty"`
+	Embedded struct {
+		Permissions []*Permission `json:"permissions,omitempty"`
+	} `json:"_embedded,omitempty"`
+	Links PermissionLinks `json:"_links,omitempty"`
+}
+
+// PermissionsService operates over permission resources.
+type PermissionsService service
+
+// Get returns a permission by its id.
+//
+// See: https://docs.mollie.com/reference/v2/permissions-api/get-permission
+func (ps *PermissionsService) Get(id string) (p *Permission, err error) {
+	u := fmt.Sprintf("v2/permissions/%s", id)
+	req, err := ps.client.NewAPIRequest(http.MethodGet, u, nil)
+	if err != nil {
+		return
+	}
+	res, err := ps.client.Do(req)
+	if err != nil {
+		return
+	}
+	if err = json.Unmarshal(res.content, &p); err != nil {
+		return
+	}
+	return
+}
+
+// List retrieves all permissions available with the current app access token.
+// The list is not paginated.
+//
+// See: https://docs.mollie.com/reference/v2/permissions-api/list-permissions
+func (ps *PermissionsService) List() (pl *PermissionsList, err error) {
+	req, err := ps.client.NewAPIRequest(http.MethodGet, "v2/permissions", nil)
+	if err != nil {
+		return
+	}
+	res, err := ps.client.Do(req)
+	if err != nil {
+		return
+	}
+	if err = json.Unmarshal(res.content, &pl); err != nil {
+		return
+	}
+	return
+}


Test patch:
diff --git a/mollie/permissions_test.go b/mollie/permissions_test.go
new file mode 100644
index 0000000..946a16d
--- /dev/null
+++ b/mollie/permissions_test.go
@@ -0,0 +1,116 @@
+package mollie
+
+import (
+	"net/http"
+	"net/url"
+	"strings"
+	"testing"
+
+	"github.com/VictorAvelar/mollie-api-go/v2/testdata"
+)
+
+func TestPermissionsService_Get(t *testing.T) {
+	setup()
+	defer teardown()
+	id := string(PaymentsRead)
+	_ = tClient.WithAuthenticationValue("test_token")
+	tMux.HandleFunc("/v2/permissions/"+id, func(w http.ResponseWriter, r *http.Request) {
+		testHeader(t, r, AuthHeader, "Bearer test_token")
+		testMethod(t, r, "GET")
+		if _, ok := r.Header[AuthHeader]; !ok {
+			w.WriteHeader(http.StatusUnauthorized)
+		}
+
+		w.WriteHeader(http.StatusOK)
+		_, _ = w.Write([]byte(testdata.GetPermissionsResponse))
+	})
+
+	p, err := tClient.Permissions.Get(id)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if p.ID != PaymentsRead {
+		t.Errorf("the response content doesn't match expectations")
+	}
+}
+
+func TestPermissionsService_List(t *testing.T) {
+	setup()
+	defer teardown()
+	_ = tClient.WithAuthenticationValue("test_token")
+	tMux.HandleFunc("/v2/permissions", func(w http.ResponseWriter, r *http.Request) {
+		testHeader(t, r, AuthHeader, "Bearer test_token")
+		testMethod(t, r, "GET")
+		if _, ok := r.Header[AuthHeader]; !ok {
+			w.WriteHeader(http.StatusUnauthorized)
+		}
+
+		w.WriteHeader(http.StatusOK)
+		_, _ = w.Write([]byte(testdata.ListPermissionsResponse))
+	})
+
+	pl, err := tClient.Permissions.List()
+	if err != nil {
+		t.Error(err)
+	}
+
+	if pl.Count != 15 {
+		t.Errorf("the response content doesn't match expectations")
+	}
+}
+
+func TestPermissionsService_HttpRequestErrors(t *testing.T) {
+	setup()
+	defer teardown()
+	tMux.HandleFunc("/v2/permissions", errorHandler)
+
+	_, gerr := tClient.Permissions.Get("payments.read")
+	_, lerr := tClient.Permissions.List()
+
+	tests := []error{lerr, gerr}
+
+	for _, tt := range tests {
+		if tt == nil {
+			t.Fail()
+		}
+	}
+}
+
+func TestPermissionsService_NewAPIRequestErrors(t *testing.T) {
+	setup()
+	defer teardown()
+	u, _ := url.Parse(tServer.URL)
+	tClient.BaseURL = u
+	tMux.HandleFunc("/v2/permissions", errorHandler)
+
+	_, gerr := tClient.Permissions.Get("payments.read")
+	_, lerr := tClient.Permissions.List()
+
+	tests := []error{lerr, gerr}
+
+	for _, tt := range tests {
+		if tt != errBadBaseURL {
+			t.Fail()
+		}
+	}
+}
+
+func TestPermissionsService_EncodingResponseErrors(t *testing.T) {
+	setup()
+	defer teardown()
+	tMux.HandleFunc("/v2/permissions/", encodingHandler)
+
+	_, gerr := tClient.Permissions.Get("payments.read")
+	_, lerr := tClient.Permissions.List()
+
+	tests := []error{lerr, gerr}
+
+	for _, tt := range tests {
+		if tt == nil {
+			t.Fail()
+		} else if !strings.Contains(tt.Error(), "invalid character") {
+			t.Errorf("unexpected error %v", tt)
+		}
+	}
+}
diff --git a/testdata/permissions.go b/testdata/permissions.go
new file mode 100644
index 0000000..541a16e
--- /dev/null
+++ b/testdata/permissions.go
@@ -0,0 +1,64 @@
+package testdata
+
+// GetPermissionsResponse example.
+const GetPermissionsResponse = `{
+    "resource": "permission",
+    "id": "payments.read",
+    "description": "View your payments",
+    "granted": true,
+    "_links": {
+        "self": {
+            "href": "https://api.mollie.com/v2/permissions/payments.read",
+            "type": "application/hal+json"
+        },
+        "documentation": {
+            "href": "https://docs.mollie.com/reference/v2/permissions-api/get-permission",
+            "type": "text/html"
+        }
+    }
+}
+`
+
+// ListPermissionsResponse example.
+const ListPermissionsResponse = `{
+    "_embedded": {
+        "permissions": [
+            {
+                "resource": "permission",
+                "id": "payments.write",
+                "description": "Create new payments",
+                "granted": false,
+                "_links": {
+                    "self": {
+                        "href": "https://api.mollie.com/v2/permissions/payments.write",
+                        "type": "application/hal+json"
+                    }
+                }
+            },
+            {
+                "resource": "permission",
+                "id": "payments.read",
+                "description": "View your payments",
+                "granted": true,
+                "_links": {
+                    "self": {
+                        "href": "https://api.mollie.com/v2/permissions/payments.read",
+                        "type": "application/hal+json"
+                    }
+                }
+            }
+       ]
+    },
+    "count": 15,
+    "_links": {
+        "documentation": {
+            "href": "https://docs.mollie.com/reference/v2/permissions-api/list-permissions",
+            "type": "text/html"
+        },
+        "self": {
+            "href": "https://api.mollie.com/v2/permissions",
+            "type": "application/hal+json"
+        }
+    }
+}
+`