Repository: nicklaw5/helix
Pull request #67 (2020-11-28 02:21:42)

Added code entitlement status.
Unable to expand on negative tests, as I don't have the necessary Twitch scenario to test this IRL to see what the possible returns are.
Access is controlled via an app access token on the calling service. The client ID associated with the app access token must be approved by Twitch as part of a contracted arrangement.

@nicklaw5 let me know how you want to handle the additional test cases.

Docs: https://dev.twitch.tv/docs/api/reference#get-code-status



Patch:
diff --git a/README.md b/README.md
index 2d25beb..13d872b 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,7 @@ looking for the Twitch API docs, see the [Twitch Developer website](https://dev.
 - [x] Get Clip
 - [ ] Get Clips
 - [x] Create Entitlement Grants Upload URL
-- [ ] Get Code Status
+- [x] Get Code Status
 - [x] Get Drops Entitlements
 - [ ] Redeem Code
 - [x] Get Top Games
diff --git a/entitlement_codes.go b/entitlement_codes.go
new file mode 100644
index 0000000..91b36d2
--- /dev/null
+++ b/entitlement_codes.go
@@ -0,0 +1,57 @@
+package helix
+
+type EntitlementCodeStatus string
+
+const (
+	SUCCESSFULLY_REDEEMED EntitlementCodeStatus = "SUCCESSFULLY_REDEEMED"
+	ALREADY_CLAIMED                             = "ALREADY_CLAIMED"
+	EXPIRED                                     = "EXPIRED"
+	USER_NOT_ELIGIBLE                           = "USER_NOT_ELIGIBLE"
+	NOT_FOUND                                   = "NOT_FOUND"
+	INACTIVE                                    = "INACTIVE"
+	UNUSED                                      = "UNUSED"
+	INCORRECT_FORMAT                            = "INCORRECT_FORMAT"
+	INTERNAL_ERROR                              = "INTERNAL_ERROR"
+)
+
+// CodesParams ...
+type CodesParams struct {
+	// One of the below
+	UserID string   `query:"user_id"`
+	Codes  []string `query:"code"` // Limit 20
+}
+
+// CodeStatus ...
+type CodeStatus struct {
+	Code   string                `json:"code"`
+	Status EntitlementCodeStatus `json:"status"`
+}
+
+// ManyCodes ...
+type ManyCodes struct {
+	Codes []CodeStatus `json:"data"`
+}
+
+// CodeResponse ...
+type CodeResponse struct {
+	ResponseCommon
+	Data ManyCodes
+}
+
+/**
+Per https://dev.twitch.tv/docs/api/reference#get-code-status
+Access is controlled via an app access token on the calling service. The client ID associated with the app access token must be approved by Twitch as part of a contracted arrangement.
+Callers with an app access token are authorized to redeem codes on behalf of any Twitch user account.
+*/
+func (c *Client) GetEntitlementCodeStatus(params *CodesParams) (*CodeResponse, error) {
+	resp, err := c.get("/entitlements/codes", &ManyCodes{}, params)
+	if err != nil {
+		return nil, err
+	}
+
+	codes := &CodeResponse{}
+	resp.HydrateResponseCommon(&codes.ResponseCommon)
+	codes.Data.Codes = resp.Data.(*ManyCodes).Codes
+
+	return codes, nil
+}
diff --git a/helix.go b/helix.go
index 00e4c14..62b9b64 100644
--- a/helix.go
+++ b/helix.go
@@ -106,8 +106,8 @@ type Pagination struct {
 	Cursor string `json:"cursor"`
 }
 
-// NewClient returns a new Twicth Helix API client. It returns an
-// if clientID is an empty string. It is concurrecy safe.
+// NewClient returns a new Twitch Helix API client. It returns an
+// if clientID is an empty string. It is concurrency safe.
 func NewClient(options *Options) (*Client, error) {
 	if options.ClientID == "" {
 		return nil, errors.New("A client ID was not provided but is required")


Test patch:
diff --git a/entitlement_codes_test.go b/entitlement_codes_test.go
new file mode 100644
index 0000000..6db74d3
--- /dev/null
+++ b/entitlement_codes_test.go
@@ -0,0 +1,62 @@
+package helix
+
+import (
+	"net/http"
+	"testing"
+)
+
+func TestClient_GetEntitlementCodeStatus(t *testing.T) {
+	t.Parallel()
+
+	testCases := []struct {
+		statusCode int
+		options    *Options
+		count      int
+		userId     string
+		codes      []string
+		respBody   string
+	}{
+		// TODO: expand with other test cases, including negative scenarios
+		{
+			http.StatusOK,
+			&Options{ClientID: "my-client-id"},
+			2,
+			"156900877",
+			[]string{"KUHXV-4GXYP-AKAKK", "XZDDZ-5SIQR-RT5M3"},
+			`{"data":[{"code":"KUHXV-4GXYP-AKAKK","status":"UNUSED"},{"code":"XZDDZ-5SIQR-RT5M3","status":"ALREADY_CLAIMED"}]}`,
+		},
+	}
+
+	for _, testCase := range testCases {
+		c := newMockClient(testCase.options, newMockHandler(testCase.statusCode, testCase.respBody, nil))
+
+		params := &CodesParams{
+			UserID: testCase.userId,
+			Codes:  testCase.codes,
+		}
+
+		resp, err := c.GetEntitlementCodeStatus(params)
+		if err != nil {
+			t.Error(err)
+		}
+
+		if resp.StatusCode != testCase.statusCode {
+			t.Errorf("expected status code to be \"%d\", got \"%d\"", testCase.statusCode, resp.StatusCode)
+		}
+
+		// TODO: Test error cases
+
+		// Test success cases
+		if len(resp.Data.Codes) != testCase.count {
+			t.Errorf("expected number of results to be \"%d\", got \"%d\"", testCase.count, len(resp.Data.Codes))
+		}
+
+		for index, code := range testCase.codes {
+			codes := resp.Data.Codes[index]
+			if codes.Code != code {
+				t.Error("expected entitlement code \"#{code}\", got \"#{codes.Code}\"")
+			}
+		}
+
+	}
+}