github.com/cli/cli@v1.14.1-0.20210902173923-1af6a669e342/pkg/httpmock/stub.go (about) 1 package httpmock 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "io" 7 "io/ioutil" 8 "net/http" 9 "os" 10 "regexp" 11 "strings" 12 ) 13 14 type Matcher func(req *http.Request) bool 15 type Responder func(req *http.Request) (*http.Response, error) 16 17 type Stub struct { 18 matched bool 19 Matcher Matcher 20 Responder Responder 21 } 22 23 func MatchAny(*http.Request) bool { 24 return true 25 } 26 27 func REST(method, p string) Matcher { 28 return func(req *http.Request) bool { 29 if !strings.EqualFold(req.Method, method) { 30 return false 31 } 32 if req.URL.Path != "/"+p { 33 return false 34 } 35 return true 36 } 37 } 38 39 func GraphQL(q string) Matcher { 40 re := regexp.MustCompile(q) 41 42 return func(req *http.Request) bool { 43 if !strings.EqualFold(req.Method, "POST") { 44 return false 45 } 46 if req.URL.Path != "/graphql" && req.URL.Path != "/api/graphql" { 47 return false 48 } 49 50 var bodyData struct { 51 Query string 52 } 53 _ = decodeJSONBody(req, &bodyData) 54 55 return re.MatchString(bodyData.Query) 56 } 57 } 58 59 func readBody(req *http.Request) ([]byte, error) { 60 bodyCopy := &bytes.Buffer{} 61 r := io.TeeReader(req.Body, bodyCopy) 62 req.Body = ioutil.NopCloser(bodyCopy) 63 return ioutil.ReadAll(r) 64 } 65 66 func decodeJSONBody(req *http.Request, dest interface{}) error { 67 b, err := readBody(req) 68 if err != nil { 69 return err 70 } 71 return json.Unmarshal(b, dest) 72 } 73 74 func StringResponse(body string) Responder { 75 return func(req *http.Request) (*http.Response, error) { 76 return httpResponse(200, req, bytes.NewBufferString(body)), nil 77 } 78 } 79 80 func StatusStringResponse(status int, body string) Responder { 81 return func(req *http.Request) (*http.Response, error) { 82 return httpResponse(status, req, bytes.NewBufferString(body)), nil 83 } 84 } 85 86 func JSONResponse(body interface{}) Responder { 87 return func(req *http.Request) (*http.Response, error) { 88 b, _ := json.Marshal(body) 89 return httpResponse(200, req, bytes.NewBuffer(b)), nil 90 } 91 } 92 93 func FileResponse(filename string) Responder { 94 return func(req *http.Request) (*http.Response, error) { 95 f, err := os.Open(filename) 96 if err != nil { 97 return nil, err 98 } 99 return httpResponse(200, req, f), nil 100 } 101 } 102 103 func RESTPayload(responseStatus int, responseBody string, cb func(payload map[string]interface{})) Responder { 104 return func(req *http.Request) (*http.Response, error) { 105 bodyData := make(map[string]interface{}) 106 err := decodeJSONBody(req, &bodyData) 107 if err != nil { 108 return nil, err 109 } 110 cb(bodyData) 111 return httpResponse(responseStatus, req, bytes.NewBufferString(responseBody)), nil 112 } 113 } 114 func GraphQLMutation(body string, cb func(map[string]interface{})) Responder { 115 return func(req *http.Request) (*http.Response, error) { 116 var bodyData struct { 117 Variables struct { 118 Input map[string]interface{} 119 } 120 } 121 err := decodeJSONBody(req, &bodyData) 122 if err != nil { 123 return nil, err 124 } 125 cb(bodyData.Variables.Input) 126 127 return httpResponse(200, req, bytes.NewBufferString(body)), nil 128 } 129 } 130 131 func GraphQLQuery(body string, cb func(string, map[string]interface{})) Responder { 132 return func(req *http.Request) (*http.Response, error) { 133 var bodyData struct { 134 Query string 135 Variables map[string]interface{} 136 } 137 err := decodeJSONBody(req, &bodyData) 138 if err != nil { 139 return nil, err 140 } 141 cb(bodyData.Query, bodyData.Variables) 142 143 return httpResponse(200, req, bytes.NewBufferString(body)), nil 144 } 145 } 146 147 func ScopesResponder(scopes string) func(*http.Request) (*http.Response, error) { 148 return func(req *http.Request) (*http.Response, error) { 149 return &http.Response{ 150 StatusCode: 200, 151 Request: req, 152 Header: map[string][]string{ 153 "X-Oauth-Scopes": {scopes}, 154 }, 155 Body: ioutil.NopCloser(bytes.NewBufferString("")), 156 }, nil 157 } 158 } 159 160 func httpResponse(status int, req *http.Request, body io.Reader) *http.Response { 161 return &http.Response{ 162 StatusCode: status, 163 Request: req, 164 Body: ioutil.NopCloser(body), 165 } 166 }