stash/pkg/plugin/js/gql.go
SmallCoccinelle 87709fd018
Errcheck phase 1 (#1715)
* Avoid redundant logging in migrations

Return the error and let the caller handle the logging of the error if
needed.

While here, defer m.Close() to the function boundary.

* Treat errors as values

Use %v rather than %s and pass the errors directly.

* Generate a wrapped error on stat-failure

* Log 3 unchecked errors

Rather than ignore errors, log them at
the WARNING log level.

The server has been functioning without these, so assume they are not at
the ERROR level.

* Propagate errors upward

Failure in path generation was ignored. Propagate the errors upward the
call stack, so it can be handled at the level of orchestration.

* Warn on errors

Log errors rather than quenching them.

Errors are logged at the Warn-level for now.

* Check error when creating test databases

Use the builtin log package and stop the program fatally on error.

* Add warnings to uncheck task errors

Focus on the task system in a single commit, logging unchecked
errors as warnings.

* Warn-on-error in API routes

Look through the API routes, and make sure errors are being logged if
they occur. Prefer the Warn-log-level because none of these has proven
to be fatal in the system up until now.

* Propagate error when adding Util API

* Propagate error on adding util API

* Return unhandled error

* JS log API: propagate and log errors

* JS Plugins: log GQL addition failures.

* Warn on failure to write to stdin

* Warn on failure to stop task

* Wrap viper.BindEnv

The current viper code only errors if no name is provided, so it should
never fail. Rewrite the code flow to factor through a panic-function.

This removes error warnings from this part of the code.

* Log errors in concurrency test

If we can't initialize the configuration, treat the test as a failure.

* Warn on errors in configuration code

* Plug an unchecked error in gallery zip walking

* Warn on screenshot serving failure

* Warn on encoder screenshot failure

* Warn on errors in path-handling code

* Undo the errcheck on configurations for now.

* Use one-line initializers where applicable

rather than using

  err := f()
  if err!= nil { ..

prefer the shorter

  if err := f(); err != nil { ..

If f() isn't too long of a name, or wraps a function with a body.
2021-09-21 09:34:25 +10:00

117 lines
2.6 KiB
Go

package js
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"strings"
"github.com/robertkrimen/otto"
)
type responseWriter struct {
r strings.Builder
header http.Header
statusCode int
}
func (w *responseWriter) Header() http.Header {
return w.header
}
func (w *responseWriter) WriteHeader(statusCode int) {
w.statusCode = statusCode
}
func (w *responseWriter) Write(b []byte) (int, error) {
return w.r.Write(b)
}
func throw(vm *otto.Otto, str string) {
value, _ := vm.Call("new Error", nil, str)
panic(value)
}
func gqlRequestFunc(vm *otto.Otto, cookie *http.Cookie, gqlHandler http.Handler) func(call otto.FunctionCall) otto.Value {
return func(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) == 0 {
throw(vm, "missing argument")
}
query := call.Argument(0)
vars := call.Argument(1)
var variables map[string]interface{}
if !vars.IsUndefined() {
exported, _ := vars.Export()
variables, _ = exported.(map[string]interface{})
}
in := struct {
Query string `json:"query"`
Variables map[string]interface{} `json:"variables,omitempty"`
}{
Query: query.String(),
Variables: variables,
}
var body bytes.Buffer
err := json.NewEncoder(&body).Encode(in)
if err != nil {
throw(vm, err.Error())
}
r, err := http.NewRequest("POST", "/graphql", &body)
if err != nil {
throw(vm, "could not make request")
}
r.Header.Set("Content-Type", "application/json")
if cookie != nil {
r.AddCookie(cookie)
}
w := &responseWriter{
header: make(http.Header),
}
gqlHandler.ServeHTTP(w, r)
if w.statusCode != http.StatusOK && w.statusCode != 0 {
throw(vm, fmt.Sprintf("graphQL query failed: %d - %s. Query: %s. Variables: %v", w.statusCode, w.r.String(), in.Query, in.Variables))
}
output := w.r.String()
// convert to JSON
var obj map[string]interface{}
if err = json.Unmarshal([]byte(output), &obj); err != nil {
throw(vm, fmt.Sprintf("could not unmarshal object %s: %s", output, err.Error()))
}
retErr, hasErr := obj["error"]
if hasErr {
throw(vm, fmt.Sprintf("graphql error: %v", retErr))
}
v, err := vm.ToValue(obj["data"])
if err != nil {
throw(vm, fmt.Sprintf("could not create return value: %s", err.Error()))
}
return v
}
}
func AddGQLAPI(vm *otto.Otto, cookie *http.Cookie, gqlHandler http.Handler) error {
gql, _ := vm.Object("({})")
if err := gql.Set("Do", gqlRequestFunc(vm, cookie, gqlHandler)); err != nil {
return fmt.Errorf("unable to set GraphQL Do function: %w", err)
}
if err := vm.Set("gql", gql); err != nil {
return fmt.Errorf("unable to set gql: %w", err)
}
return nil
}