diff options
| author | mo khan <mo@mokhan.ca> | 2025-05-14 13:18:54 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-05-14 13:18:54 -0600 |
| commit | 4b2d609a0efcc1d9b2f1a08f954d067ad1d9cd1e (patch) | |
| tree | 0afacf9217b0569130da6b97d4197331681bf119 /vendor/github.com/playwright-community/playwright-go/fetch.go | |
| parent | ab373d1fe698cd3f53258c09bc8515d88a6d0b9e (diff) | |
test: use playwright to test out an OIDC login
Diffstat (limited to 'vendor/github.com/playwright-community/playwright-go/fetch.go')
| -rw-r--r-- | vendor/github.com/playwright-community/playwright-go/fetch.go | 451 |
1 files changed, 451 insertions, 0 deletions
diff --git a/vendor/github.com/playwright-community/playwright-go/fetch.go b/vendor/github.com/playwright-community/playwright-go/fetch.go new file mode 100644 index 0000000..fc7f79f --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/fetch.go @@ -0,0 +1,451 @@ +package playwright + +import ( + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "os" + "strings" +) + +type apiRequestImpl struct { + *Playwright +} + +func (r *apiRequestImpl) NewContext(options ...APIRequestNewContextOptions) (APIRequestContext, error) { + overrides := map[string]interface{}{} + if len(options) == 1 { + if options[0].ClientCertificates != nil { + certs, err := transformClientCertificate(options[0].ClientCertificates) + if err != nil { + return nil, err + } + overrides["clientCertificates"] = certs + options[0].ClientCertificates = nil + } + if options[0].ExtraHttpHeaders != nil { + overrides["extraHTTPHeaders"] = serializeMapToNameAndValue(options[0].ExtraHttpHeaders) + options[0].ExtraHttpHeaders = nil + } + if options[0].StorageStatePath != nil { + var storageState *StorageState + storageString, err := os.ReadFile(*options[0].StorageStatePath) + if err != nil { + return nil, fmt.Errorf("could not read storage state file: %w", err) + } + err = json.Unmarshal(storageString, &storageState) + if err != nil { + return nil, fmt.Errorf("could not parse storage state file: %w", err) + } + options[0].StorageState = storageState + options[0].StorageStatePath = nil + } + } + + channel, err := r.channel.Send("newRequest", options, overrides) + if err != nil { + return nil, err + } + return fromChannel(channel).(*apiRequestContextImpl), nil +} + +func newApiRequestImpl(pw *Playwright) *apiRequestImpl { + return &apiRequestImpl{pw} +} + +type apiRequestContextImpl struct { + channelOwner + tracing *tracingImpl + closeReason *string +} + +func (r *apiRequestContextImpl) Dispose(options ...APIRequestContextDisposeOptions) error { + if len(options) == 1 { + r.closeReason = options[0].Reason + } + _, err := r.channel.Send("dispose", map[string]interface{}{ + "reason": r.closeReason, + }) + if errors.Is(err, ErrTargetClosed) { + return nil + } + return err +} + +func (r *apiRequestContextImpl) Delete(url string, options ...APIRequestContextDeleteOptions) (APIResponse, error) { + opts := APIRequestContextFetchOptions{ + Method: String("DELETE"), + } + if len(options) == 1 { + err := assignStructFields(&opts, options[0], false) + if err != nil { + return nil, err + } + } + + return r.Fetch(url, opts) +} + +func (r *apiRequestContextImpl) Fetch(urlOrRequest interface{}, options ...APIRequestContextFetchOptions) (APIResponse, error) { + switch v := urlOrRequest.(type) { + case string: + return r.innerFetch(v, nil, options...) + case Request: + return r.innerFetch("", v, options...) + default: + return nil, fmt.Errorf("urlOrRequest has unsupported type: %T", urlOrRequest) + } +} + +func (r *apiRequestContextImpl) innerFetch(url string, request Request, options ...APIRequestContextFetchOptions) (APIResponse, error) { + if r.closeReason != nil { + return nil, fmt.Errorf("%w: %s", ErrTargetClosed, *r.closeReason) + } + overrides := map[string]interface{}{} + if url != "" { + overrides["url"] = url + } else if request != nil { + overrides["url"] = request.URL() + } + + if len(options) == 1 { + if options[0].MaxRedirects != nil && *options[0].MaxRedirects < 0 { + return nil, errors.New("maxRedirects must be non-negative") + } + if options[0].MaxRetries != nil && *options[0].MaxRetries < 0 { + return nil, errors.New("maxRetries must be non-negative") + } + // only one of them can be specified + if countNonNil(options[0].Data, options[0].Form, options[0].Multipart) > 1 { + return nil, errors.New("only one of 'data', 'form' or 'multipart' can be specified") + } + if options[0].Method == nil { + if request != nil { + options[0].Method = String(request.Method()) + } else { + options[0].Method = String("GET") + } + } + if options[0].Headers == nil { + if request != nil { + overrides["headers"] = serializeMapToNameAndValue(request.Headers()) + } + } else { + overrides["headers"] = serializeMapToNameAndValue(options[0].Headers) + options[0].Headers = nil + } + if options[0].Data != nil { + switch v := options[0].Data.(type) { + case string: + headersArray, ok := overrides["headers"].([]map[string]string) + if ok && isJsonContentType(headersArray) { + if json.Valid([]byte(v)) { + overrides["jsonData"] = v + } else { + data, err := json.Marshal(v) + if err != nil { + return nil, fmt.Errorf("could not marshal data: %w", err) + } + overrides["jsonData"] = string(data) + } + } else { + overrides["postData"] = base64.StdEncoding.EncodeToString([]byte(v)) + } + case []byte: + overrides["postData"] = base64.StdEncoding.EncodeToString(v) + case interface{}: + data, err := json.Marshal(v) + if err != nil { + return nil, fmt.Errorf("could not marshal data: %w", err) + } + overrides["jsonData"] = string(data) + default: + return nil, errors.New("data must be a string, []byte, or interface{} that can marshal to json") + } + options[0].Data = nil + } else if options[0].Form != nil { + form, ok := options[0].Form.(map[string]interface{}) + if !ok { + return nil, errors.New("form must be a map") + } + overrides["formData"] = serializeMapToNameValue(form) + options[0].Form = nil + } else if options[0].Multipart != nil { + _, ok := options[0].Multipart.(map[string]interface{}) + if !ok { + return nil, errors.New("multipart must be a map") + } + multipartData := []map[string]interface{}{} + for name, value := range options[0].Multipart.(map[string]interface{}) { + switch v := value.(type) { + case InputFile: + multipartData = append(multipartData, map[string]interface{}{ + "name": name, + "file": map[string]string{ + "name": v.Name, + "mimeType": v.MimeType, + "buffer": base64.StdEncoding.EncodeToString(v.Buffer), + }, + }) + default: + multipartData = append(multipartData, map[string]interface{}{ + "name": name, + "value": String(fmt.Sprintf("%v", v)), + }) + } + } + overrides["multipartData"] = multipartData + options[0].Multipart = nil + } else if request != nil { + postDataBuf, err := request.PostDataBuffer() + if err == nil { + overrides["postData"] = base64.StdEncoding.EncodeToString(postDataBuf) + } + } + if options[0].Params != nil { + overrides["params"] = serializeMapToNameValue(options[0].Params) + options[0].Params = nil + } + } + + response, err := r.channel.Send("fetch", options, overrides) + if err != nil { + return nil, err + } + + return newAPIResponse(r, response.(map[string]interface{})), nil +} + +func (r *apiRequestContextImpl) Get(url string, options ...APIRequestContextGetOptions) (APIResponse, error) { + opts := APIRequestContextFetchOptions{ + Method: String("GET"), + } + if len(options) == 1 { + err := assignStructFields(&opts, options[0], false) + if err != nil { + return nil, err + } + } + + return r.Fetch(url, opts) +} + +func (r *apiRequestContextImpl) Head(url string, options ...APIRequestContextHeadOptions) (APIResponse, error) { + opts := APIRequestContextFetchOptions{ + Method: String("HEAD"), + } + if len(options) == 1 { + err := assignStructFields(&opts, options[0], false) + if err != nil { + return nil, err + } + } + + return r.Fetch(url, opts) +} + +func (r *apiRequestContextImpl) Patch(url string, options ...APIRequestContextPatchOptions) (APIResponse, error) { + opts := APIRequestContextFetchOptions{ + Method: String("PATCH"), + } + if len(options) == 1 { + err := assignStructFields(&opts, options[0], false) + if err != nil { + return nil, err + } + } + + return r.Fetch(url, opts) +} + +func (r *apiRequestContextImpl) Put(url string, options ...APIRequestContextPutOptions) (APIResponse, error) { + opts := APIRequestContextFetchOptions{ + Method: String("PUT"), + } + if len(options) == 1 { + err := assignStructFields(&opts, options[0], false) + if err != nil { + return nil, err + } + } + + return r.Fetch(url, opts) +} + +func (r *apiRequestContextImpl) Post(url string, options ...APIRequestContextPostOptions) (APIResponse, error) { + opts := APIRequestContextFetchOptions{ + Method: String("POST"), + } + if len(options) == 1 { + err := assignStructFields(&opts, options[0], false) + if err != nil { + return nil, err + } + } + + return r.Fetch(url, opts) +} + +func (r *apiRequestContextImpl) StorageState(path ...string) (*StorageState, error) { + result, err := r.channel.SendReturnAsDict("storageState") + if err != nil { + return nil, err + } + if len(path) == 1 { + file, err := os.Create(path[0]) + if err != nil { + return nil, err + } + if err := json.NewEncoder(file).Encode(result); err != nil { + return nil, err + } + if err := file.Close(); err != nil { + return nil, err + } + } + var storageState StorageState + remapMapToStruct(result, &storageState) + return &storageState, nil +} + +func newAPIRequestContext(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *apiRequestContextImpl { + rc := &apiRequestContextImpl{} + rc.createChannelOwner(rc, parent, objectType, guid, initializer) + rc.tracing = fromChannel(initializer["tracing"]).(*tracingImpl) + return rc +} + +type apiResponseImpl struct { + request *apiRequestContextImpl + initializer map[string]interface{} + headers *rawHeaders +} + +func (r *apiResponseImpl) Body() ([]byte, error) { + result, err := r.request.channel.SendReturnAsDict("fetchResponseBody", []map[string]interface{}{ + { + "fetchUid": r.fetchUid(), + }, + }) + if err != nil { + if errors.Is(err, ErrTargetClosed) { + return nil, errors.New("response has been disposed") + } + return nil, err + } + body := result["binary"] + if body == nil { + return nil, errors.New("response has been disposed") + } + return base64.StdEncoding.DecodeString(body.(string)) +} + +func (r *apiResponseImpl) Dispose() error { + _, err := r.request.channel.Send("disposeAPIResponse", []map[string]interface{}{ + { + "fetchUid": r.fetchUid(), + }, + }) + return err +} + +func (r *apiResponseImpl) Headers() map[string]string { + return r.headers.Headers() +} + +func (r *apiResponseImpl) HeadersArray() []NameValue { + return r.headers.HeadersArray() +} + +func (r *apiResponseImpl) JSON(v interface{}) error { + body, err := r.Body() + if err != nil { + return err + } + return json.Unmarshal(body, &v) +} + +func (r *apiResponseImpl) Ok() bool { + return r.Status() == 0 || (r.Status() >= 200 && r.Status() <= 299) +} + +func (r *apiResponseImpl) Status() int { + return int(r.initializer["status"].(float64)) +} + +func (r *apiResponseImpl) StatusText() string { + return r.initializer["statusText"].(string) +} + +func (r *apiResponseImpl) Text() (string, error) { + body, err := r.Body() + if err != nil { + return "", err + } + return string(body), nil +} + +func (r *apiResponseImpl) URL() string { + return r.initializer["url"].(string) +} + +func (r *apiResponseImpl) fetchUid() string { + return r.initializer["fetchUid"].(string) +} + +func (r *apiResponseImpl) fetchLog() ([]string, error) { + ret, err := r.request.channel.Send("fetchLog", map[string]interface{}{ + "fetchUid": r.fetchUid(), + }) + if err != nil { + return nil, err + } + result := make([]string, len(ret.([]interface{}))) + for i, v := range ret.([]interface{}) { + result[i] = v.(string) + } + return result, nil +} + +func newAPIResponse(context *apiRequestContextImpl, initializer map[string]interface{}) *apiResponseImpl { + return &apiResponseImpl{ + request: context, + initializer: initializer, + headers: newRawHeaders(initializer["headers"]), + } +} + +func countNonNil(args ...interface{}) int { + count := 0 + for _, v := range args { + if v != nil { + count++ + } + } + return count +} + +func isJsonContentType(headers []map[string]string) bool { + if len(headers) > 0 { + for _, v := range headers { + if strings.ToLower(v["name"]) == "content-type" { + if v["value"] == "application/json" { + return true + } + } + } + } + return false +} + +func serializeMapToNameValue(data map[string]interface{}) []map[string]string { + serialized := make([]map[string]string, 0, len(data)) + for k, v := range data { + serialized = append(serialized, map[string]string{ + "name": k, + "value": fmt.Sprintf("%v", v), + }) + } + return serialized +} |
