Skip to content

Commit d0ee313

Browse files
SiteShield API added
1 parent 093f835 commit d0ee313

File tree

8 files changed

+746
-0
lines changed

8 files changed

+746
-0
lines changed

pkg/siteshield/errors.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package siteshield
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"fmt"
7+
"io/ioutil"
8+
"net/http"
9+
)
10+
11+
var (
12+
// ErrBadRequest is returned when a required parameter is missing
13+
ErrBadRequest = errors.New("missing argument")
14+
)
15+
16+
type (
17+
// Error is a appsec error interface
18+
Error struct {
19+
Type string `json:"type"`
20+
Title string `json:"title"`
21+
Detail string `json:"detail"`
22+
Instance string `json:"instance,omitempty"`
23+
BehaviorName string `json:"behaviorName,omitempty"`
24+
ErrorLocation string `json:"errorLocation,omitempty"`
25+
StatusCode int `json:"-"`
26+
}
27+
)
28+
29+
// Error parses an error from the response
30+
func (s *siteshieldmap) Error(r *http.Response) error {
31+
var e Error
32+
33+
var body []byte
34+
35+
body, err := ioutil.ReadAll(r.Body)
36+
if err != nil {
37+
s.Log(r.Request.Context()).Errorf("reading error response body: %s", err)
38+
e.StatusCode = r.StatusCode
39+
e.Title = fmt.Sprintf("Failed to read error body")
40+
e.Detail = err.Error()
41+
return &e
42+
}
43+
44+
if err := json.Unmarshal(body, &e); err != nil {
45+
s.Log(r.Request.Context()).Errorf("could not unmarshal API error: %s", err)
46+
e.Title = fmt.Sprintf("Failed to unmarshal error body")
47+
e.Detail = err.Error()
48+
}
49+
50+
e.StatusCode = r.StatusCode
51+
52+
return &e
53+
}
54+
55+
func (e *Error) Error() string {
56+
return fmt.Sprintf("Title: %s; Type: %s; Detail: %s", e.Title, e.Type, e.Detail)
57+
}
58+
59+
// Is handles error comparisons
60+
func (e *Error) Is(target error) bool {
61+
var t *Error
62+
if !errors.As(target, &t) {
63+
return false
64+
}
65+
66+
if e == t {
67+
return true
68+
}
69+
70+
if e.StatusCode != t.StatusCode {
71+
return false
72+
}
73+
74+
return e.Error() == t.Error()
75+
}

pkg/siteshield/errors_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package siteshield
2+
3+
import (
4+
"context"
5+
"io/ioutil"
6+
"net/http"
7+
"strings"
8+
"testing"
9+
10+
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v2/pkg/session"
11+
"github.com/stretchr/testify/require"
12+
"github.com/tj/assert"
13+
)
14+
15+
func TestNewError(t *testing.T) {
16+
sess, err := session.New()
17+
require.NoError(t, err)
18+
19+
req, err := http.NewRequestWithContext(
20+
context.TODO(),
21+
http.MethodHead,
22+
"/",
23+
nil)
24+
require.NoError(t, err)
25+
26+
tests := map[string]struct {
27+
response *http.Response
28+
expected *Error
29+
}{
30+
"valid response, status code 500": {
31+
response: &http.Response{
32+
Status: "Internal Server Error",
33+
StatusCode: http.StatusInternalServerError,
34+
Body: ioutil.NopCloser(strings.NewReader(
35+
`{"type":"a","title":"b","detail":"c"}`),
36+
),
37+
Request: req,
38+
},
39+
expected: &Error{
40+
Type: "a",
41+
Title: "b",
42+
Detail: "c",
43+
StatusCode: http.StatusInternalServerError,
44+
},
45+
},
46+
"invalid response body, assign status code": {
47+
response: &http.Response{
48+
Status: "Internal Server Error",
49+
StatusCode: http.StatusInternalServerError,
50+
Body: ioutil.NopCloser(strings.NewReader(
51+
`test`),
52+
),
53+
Request: req,
54+
},
55+
expected: &Error{
56+
Title: "Failed to unmarshal error body",
57+
Detail: "invalid character 'e' in literal true (expecting 'r')",
58+
StatusCode: http.StatusInternalServerError,
59+
},
60+
},
61+
}
62+
for name, test := range tests {
63+
t.Run(name, func(t *testing.T) {
64+
res := Client(sess).(*siteshieldmap).Error(test.response)
65+
assert.Equal(t, test.expected, res)
66+
})
67+
}
68+
}

pkg/siteshield/siteshield.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Package siteshield provides access to the Akamai Site Shield APIs
2+
package siteshield
3+
4+
import (
5+
"errors"
6+
7+
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v2/pkg/session"
8+
)
9+
10+
var (
11+
// ErrStructValidation is returned returned when given struct validation failed
12+
ErrStructValidation = errors.New("struct validation")
13+
)
14+
15+
type (
16+
// SSMAPS is the siteshieldmap api interface
17+
SSMAPS interface {
18+
SiteShieldMap
19+
}
20+
21+
siteshieldmap struct {
22+
session.Session
23+
usePrefixes bool
24+
}
25+
26+
// Option defines a siteshieldmap option
27+
Option func(*siteshieldmap)
28+
29+
// ClientFunc is a siteshieldmap client new method, this can used for mocking
30+
ClientFunc func(sess session.Session, opts ...Option) SSMAPS
31+
)
32+
33+
// Client returns a new siteshieldmap Client instance with the specified controller
34+
func Client(sess session.Session, opts ...Option) SSMAPS {
35+
s := &siteshieldmap{
36+
Session: sess,
37+
}
38+
39+
for _, opt := range opts {
40+
opt(s)
41+
}
42+
return s
43+
}

pkg/siteshield/siteshield_map.go

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package siteshield
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
8+
validation "github.com/go-ozzo/ozzo-validation"
9+
)
10+
11+
// SiteShieldMap represents a collection of Site Shield
12+
//
13+
// See: SiteShieldMap.GetSiteShieldMaps()
14+
// API Docs: // site_shield v1
15+
//
16+
// https://developer.akamai.com/api/cloud_security/site_shield/v1.html
17+
18+
type (
19+
// SiteShieldMap contains operations available on SiteShieldMap resource
20+
// See: // site_shield v1
21+
//
22+
// https://developer.akamai.com/api/cloud_security/site_shield/v1.html#getamap
23+
SiteShieldMap interface {
24+
GetSiteShieldMaps(ctx context.Context) (*GetSiteShieldMapsResponse, error)
25+
GetSiteShieldMap(ctx context.Context, params SiteShieldMapRequest) (*SiteShieldMapResponse, error)
26+
AckSiteShieldMap(ctx context.Context, params SiteShieldMapRequest) (*SiteShieldMapResponse, error)
27+
}
28+
29+
SiteShieldMapRequest struct {
30+
UniqueID int
31+
}
32+
33+
GetSiteShieldMapsResponse struct {
34+
SiteShieldMaps []SiteShieldMapResponse `json:"siteShieldMaps"`
35+
}
36+
37+
SiteShieldMapResponse struct {
38+
Acknowledged bool `json:"acknowledged"`
39+
Contacts []string `json:"contacts"`
40+
CurrentCidrs []string `json:"currentCidrs"`
41+
ProposedCidrs []string `json:"proposedCidrs"`
42+
RuleName string `json:"ruleName"`
43+
Type string `json:"type"`
44+
Service string `json:"service"`
45+
Shared bool `json:"shared"`
46+
AcknowledgeRequiredBy int64 `json:"acknowledgeRequiredBy"`
47+
PreviouslyAcknowledgedOn int64 `json:"previouslyAcknowledgedOn"`
48+
ID int `json:"id,omitempty"`
49+
LatestTicketID int `json:"latestTicketId,omitempty"`
50+
MapAlias string `json:"mapAlias,omitempty"`
51+
McmMapRuleID int `json:"mcmMapRuleId,omitempty"`
52+
}
53+
)
54+
55+
// Validate validates SiteShieldMapRequest
56+
func (v SiteShieldMapRequest) Validate() error {
57+
return validation.Errors{
58+
"UniqueID": validation.Validate(v.UniqueID, validation.Required),
59+
}.Filter()
60+
}
61+
62+
// GetSiteShieldMaps will get a list of SiteShieldMap.
63+
//
64+
// API Docs: // site_shield v1
65+
//
66+
// https://developer.akamai.com/api/cloud_security/site_shield/v1.html#listmaps
67+
68+
func (s *siteshieldmap) GetSiteShieldMaps(ctx context.Context) (*GetSiteShieldMapsResponse, error) {
69+
logger := s.Log(ctx)
70+
logger.Debug("GetSiteShieldMaps")
71+
72+
var rval GetSiteShieldMapsResponse
73+
74+
uri := "/siteshield/v1/maps"
75+
76+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
77+
if err != nil {
78+
return nil, fmt.Errorf("failed to create getSiteShieldMaps request: %s", err.Error())
79+
}
80+
81+
resp, err := s.Exec(req, &rval)
82+
if err != nil {
83+
return nil, fmt.Errorf("getsiteshieldmaps request failed: %s", err.Error())
84+
}
85+
86+
if resp.StatusCode != http.StatusOK {
87+
return nil, s.Error(resp)
88+
}
89+
90+
return &rval, nil
91+
92+
}
93+
94+
// GetSiteShieldMap will get a SiteShieldMap by unique ID.
95+
//
96+
// API Docs: // site_shield v1
97+
//
98+
// https://developer.akamai.com/api/cloud_security/site_shield/v1.html#getamap
99+
100+
func (s *siteshieldmap) GetSiteShieldMap(ctx context.Context, params SiteShieldMapRequest) (*SiteShieldMapResponse, error) {
101+
if err := params.Validate(); err != nil {
102+
return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
103+
}
104+
105+
logger := s.Log(ctx)
106+
logger.Debug("GetSiteShieldMap")
107+
108+
var rval SiteShieldMapResponse
109+
110+
uri := fmt.Sprintf("/siteshield/v1/maps/%d", params.UniqueID)
111+
112+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
113+
if err != nil {
114+
return nil, fmt.Errorf("failed to create getSiteShieldMap request: %s", err.Error())
115+
}
116+
117+
resp, err := s.Exec(req, &rval)
118+
if err != nil {
119+
return nil, fmt.Errorf("getSiteShieldMap request failed: %s", err.Error())
120+
}
121+
122+
if resp.StatusCode != http.StatusOK {
123+
return nil, s.Error(resp)
124+
}
125+
126+
return &rval, nil
127+
}
128+
129+
// AckSiteShieldMap will acknowledge changes to a SiteShieldMap.
130+
//
131+
// API Docs: // site_shield v1
132+
//
133+
// https://developer.akamai.com/api/cloud_security/site_shield/v1.html#acknowledgeamap
134+
135+
func (s *siteshieldmap) AckSiteShieldMap(ctx context.Context, params SiteShieldMapRequest) (*SiteShieldMapResponse, error) {
136+
if err := params.Validate(); err != nil {
137+
return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
138+
}
139+
140+
logger := s.Log(ctx)
141+
logger.Debug("AckSiteShieldMap")
142+
143+
postURL := fmt.Sprintf("/siteshield/v1/maps/%d/acknowledge", params.UniqueID)
144+
145+
req, err := http.NewRequestWithContext(ctx, http.MethodPost, postURL, nil)
146+
if err != nil {
147+
return nil, fmt.Errorf("failed to create AckSiteShieldMap: %s", err.Error())
148+
}
149+
150+
var rval SiteShieldMapResponse
151+
resp, err := s.Exec(req, &rval, params)
152+
if err != nil {
153+
return nil, fmt.Errorf("AckSiteShieldMap request failed: %s", err.Error())
154+
}
155+
156+
if resp.StatusCode != http.StatusOK {
157+
return nil, s.Error(resp)
158+
}
159+
160+
return &rval, nil
161+
}

0 commit comments

Comments
 (0)