mirror of
https://github.com/cwinfo/matterbridge.git
synced 2024-12-26 05:15:38 +00:00
Update mattermost library (#2152)
* Update mattermost library * Fix linting
This commit is contained in:
parent
65d78e38af
commit
d16645c952
@ -214,6 +214,7 @@ linters:
|
|||||||
- exhaustive
|
- exhaustive
|
||||||
- testifylint
|
- testifylint
|
||||||
- mnd
|
- mnd
|
||||||
|
- depguard
|
||||||
# rules to deal with reported isues
|
# rules to deal with reported isues
|
||||||
issues:
|
issues:
|
||||||
# List of regexps of issue texts to exclude, empty list by default.
|
# List of regexps of issue texts to exclude, empty list by default.
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package bmattermost
|
package bmattermost
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
"github.com/42wim/matterbridge/bridge/helper"
|
"github.com/42wim/matterbridge/bridge/helper"
|
||||||
"github.com/matterbridge/matterclient"
|
"github.com/matterbridge/matterclient"
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost/server/public/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// handleDownloadAvatar downloads the avatar of userid from channel
|
// handleDownloadAvatar downloads the avatar of userid from channel
|
||||||
@ -25,7 +27,7 @@ func (b *Bmattermost) handleDownloadAvatar(userid string, channel string) {
|
|||||||
data []byte
|
data []byte
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
data, _, err = b.mc.Client.GetProfileImage(userid, "")
|
data, _, err = b.mc.Client.GetProfileImage(context.TODO(), userid, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Log.Errorf("ProfileImage download failed for %#v %s", userid, err)
|
b.Log.Errorf("ProfileImage download failed for %#v %s", userid, err)
|
||||||
return
|
return
|
||||||
@ -43,8 +45,8 @@ func (b *Bmattermost) handleDownloadAvatar(userid string, channel string) {
|
|||||||
|
|
||||||
//nolint:wrapcheck
|
//nolint:wrapcheck
|
||||||
func (b *Bmattermost) handleDownloadFile(rmsg *config.Message, id string) error {
|
func (b *Bmattermost) handleDownloadFile(rmsg *config.Message, id string) error {
|
||||||
url, _, _ := b.mc.Client.GetFileLink(id)
|
url, _, _ := b.mc.Client.GetFileLink(context.TODO(), id)
|
||||||
finfo, _, err := b.mc.Client.GetFileInfo(id)
|
finfo, _, err := b.mc.Client.GetFileInfo(context.TODO(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -52,7 +54,7 @@ func (b *Bmattermost) handleDownloadFile(rmsg *config.Message, id string) error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
data, _, err := b.mc.Client.DownloadFile(id, true)
|
data, _, err := b.mc.Client.DownloadFile(context.TODO(), id, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/42wim/matterbridge/bridge/helper"
|
"github.com/42wim/matterbridge/bridge/helper"
|
||||||
"github.com/42wim/matterbridge/matterhook"
|
"github.com/42wim/matterbridge/matterhook"
|
||||||
"github.com/matterbridge/matterclient"
|
"github.com/matterbridge/matterclient"
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost/server/public/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *Bmattermost) doConnectWebhookBind() error {
|
func (b *Bmattermost) doConnectWebhookBind() error {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package bmattermost
|
package bmattermost
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
@ -157,7 +158,7 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
|
|||||||
|
|
||||||
// we only can reply to the root of the thread, not to a specific ID (like discord for example does)
|
// we only can reply to the root of the thread, not to a specific ID (like discord for example does)
|
||||||
if msg.ParentID != "" {
|
if msg.ParentID != "" {
|
||||||
post, _, err := b.mc.Client.GetPost(msg.ParentID, "")
|
post, _, err := b.mc.Client.GetPost(context.TODO(), msg.ParentID, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Log.Errorf("getting post %s failed: %s", msg.ParentID, err)
|
b.Log.Errorf("getting post %s failed: %s", msg.ParentID, err)
|
||||||
}
|
}
|
||||||
|
60
go.mod
60
go.mod
@ -14,7 +14,7 @@ require (
|
|||||||
github.com/google/gops v0.3.27
|
github.com/google/gops v0.3.27
|
||||||
github.com/gorilla/schema v1.3.0
|
github.com/gorilla/schema v1.3.0
|
||||||
github.com/harmony-development/shibshib v0.0.0-20220101224523-c98059d09cfa
|
github.com/harmony-development/shibshib v0.0.0-20220101224523-c98059d09cfa
|
||||||
github.com/hashicorp/golang-lru v0.6.0
|
github.com/hashicorp/golang-lru v1.0.2
|
||||||
github.com/jpillora/backoff v1.0.0
|
github.com/jpillora/backoff v1.0.0
|
||||||
github.com/keybase/go-keybase-chat-bot v0.0.0-20221220212439-e48d9abd2c20
|
github.com/keybase/go-keybase-chat-bot v0.0.0-20221220212439-e48d9abd2c20
|
||||||
github.com/kyokomi/emoji/v2 v2.2.13
|
github.com/kyokomi/emoji/v2 v2.2.13
|
||||||
@ -25,9 +25,9 @@ require (
|
|||||||
github.com/matterbridge/gomatrix v0.0.0-20220411225302-271e5088ea27
|
github.com/matterbridge/gomatrix v0.0.0-20220411225302-271e5088ea27
|
||||||
github.com/matterbridge/gozulipbot v0.0.0-20211023205727-a19d6c1f3b75
|
github.com/matterbridge/gozulipbot v0.0.0-20211023205727-a19d6c1f3b75
|
||||||
github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba
|
github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba
|
||||||
github.com/matterbridge/matterclient v0.0.0-20230329213635-bc6e42a4a84a
|
github.com/matterbridge/matterclient v0.0.0-20240523235056-57f299489168
|
||||||
github.com/matterbridge/telegram-bot-api/v6 v6.5.0
|
github.com/matterbridge/telegram-bot-api/v6 v6.5.0
|
||||||
github.com/mattermost/mattermost-server/v6 v6.7.2
|
github.com/mattermost/mattermost/server/public v0.1.3
|
||||||
github.com/mattn/godown v0.0.1
|
github.com/mattn/godown v0.0.1
|
||||||
github.com/mdp/qrterminal v1.0.1
|
github.com/mdp/qrterminal v1.0.1
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
@ -62,51 +62,50 @@ require (
|
|||||||
github.com/Jeffail/gabs v1.4.0 // indirect
|
github.com/Jeffail/gabs v1.4.0 // indirect
|
||||||
github.com/apex/log v1.9.0 // indirect
|
github.com/apex/log v1.9.0 // indirect
|
||||||
github.com/av-elier/go-decimal-to-rational v0.0.0-20191127152832-89e6aad02ecf // indirect
|
github.com/av-elier/go-decimal-to-rational v0.0.0-20191127152832-89e6aad02ecf // indirect
|
||||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/dyatlov/go-opengraph v0.0.0-20210112100619-dae8665a5b09 // indirect
|
github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a // indirect
|
||||||
|
github.com/fatih/color v1.16.0 // indirect
|
||||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
|
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/gopackage/ddp v0.0.3 // indirect
|
github.com/gopackage/ddp v0.0.3 // indirect
|
||||||
github.com/gorilla/websocket v1.5.0 // indirect
|
github.com/gorilla/websocket v1.5.1 // indirect
|
||||||
github.com/graph-gophers/graphql-go v1.3.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
|
github.com/hashicorp/go-hclog v1.6.2 // indirect
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
|
github.com/hashicorp/go-plugin v1.6.0 // indirect
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||||
github.com/kettek/apng v0.0.0-20191108220231-414630eed80f // indirect
|
github.com/kettek/apng v0.0.0-20191108220231-414630eed80f // indirect
|
||||||
github.com/klauspost/compress v1.17.0 // indirect
|
github.com/klauspost/compress v1.17.7 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
|
||||||
github.com/labstack/gommon v0.4.2 // indirect
|
github.com/labstack/gommon v0.4.2 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 // indirect
|
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 // indirect
|
||||||
github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d // indirect
|
github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956 // indirect
|
||||||
github.com/mattermost/logr/v2 v2.0.15 // indirect
|
github.com/mattermost/logr/v2 v2.0.21 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||||
github.com/minio/md5-simd v1.1.2 // indirect
|
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||||
github.com/minio/minio-go/v7 v7.0.24 // indirect
|
|
||||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
|
||||||
github.com/monaco-io/request v1.0.5 // indirect
|
github.com/monaco-io/request v1.0.5 // indirect
|
||||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
github.com/nxadm/tail v1.4.11 // indirect
|
||||||
|
github.com/oklog/run v1.1.0 // indirect
|
||||||
github.com/pborman/uuid v1.2.1 // indirect
|
github.com/pborman/uuid v1.2.1 // indirect
|
||||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||||
github.com/philhofer/fwd v1.1.1 // indirect
|
github.com/philhofer/fwd v1.1.2 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/rickb777/date v1.12.4 // indirect
|
github.com/rickb777/date v1.12.4 // indirect
|
||||||
github.com/rickb777/plural v1.2.0 // indirect
|
github.com/rickb777/plural v1.2.0 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/rs/zerolog v1.32.0 // indirect
|
github.com/rs/zerolog v1.32.0 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
@ -118,25 +117,26 @@ require (
|
|||||||
github.com/spf13/cast v1.6.0 // indirect
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/tinylib/msgp v1.1.6 // indirect
|
github.com/tinylib/msgp v1.1.9 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
github.com/vmihailenco/msgpack/v5 v5.4.0 // indirect
|
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
github.com/wiggin77/merror v1.0.3 // indirect
|
github.com/wiggin77/merror v1.0.5 // indirect
|
||||||
github.com/wiggin77/srslog v1.0.1 // indirect
|
github.com/wiggin77/srslog v1.0.1 // indirect
|
||||||
go.mau.fi/libsignal v0.1.0 // indirect
|
go.mau.fi/libsignal v0.1.0 // indirect
|
||||||
go.mau.fi/util v0.4.1 // indirect
|
go.mau.fi/util v0.4.1 // indirect
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
|
||||||
golang.org/x/crypto v0.23.0 // indirect
|
golang.org/x/crypto v0.23.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f // indirect
|
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f // indirect
|
||||||
golang.org/x/net v0.25.0 // indirect
|
golang.org/x/net v0.25.0 // indirect
|
||||||
golang.org/x/sys v0.20.0 // indirect
|
golang.org/x/sys v0.20.0 // indirect
|
||||||
golang.org/x/term v0.20.0 // indirect
|
golang.org/x/term v0.20.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||||
|
google.golang.org/grpc v1.62.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||||
|
21
vendor/github.com/blang/semver/.travis.yml
generated
vendored
21
vendor/github.com/blang/semver/.travis.yml
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
language: go
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- go: 1.4.3
|
|
||||||
- go: 1.5.4
|
|
||||||
- go: 1.6.3
|
|
||||||
- go: 1.7
|
|
||||||
- go: tip
|
|
||||||
allow_failures:
|
|
||||||
- go: tip
|
|
||||||
install:
|
|
||||||
- go get golang.org/x/tools/cmd/cover
|
|
||||||
- go get github.com/mattn/goveralls
|
|
||||||
script:
|
|
||||||
- echo "Test and track coverage" ; $HOME/gopath/bin/goveralls -package "." -service=travis-ci
|
|
||||||
-repotoken $COVERALLS_TOKEN
|
|
||||||
- echo "Build examples" ; cd examples && go build
|
|
||||||
- echo "Check if gofmt'd" ; diff -u <(echo -n) <(gofmt -d -s .)
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
secure: HroGEAUQpVq9zX1b1VIkraLiywhGbzvNnTZq2TMxgK7JHP8xqNplAeF1izrR2i4QLL9nsY+9WtYss4QuPvEtZcVHUobw6XnL6radF7jS1LgfYZ9Y7oF+zogZ2I5QUMRLGA7rcxQ05s7mKq3XZQfeqaNts4bms/eZRefWuaFZbkw=
|
|
194
vendor/github.com/blang/semver/README.md
generated
vendored
194
vendor/github.com/blang/semver/README.md
generated
vendored
@ -1,194 +0,0 @@
|
|||||||
semver for golang [![Build Status](https://travis-ci.org/blang/semver.svg?branch=master)](https://travis-ci.org/blang/semver) [![GoDoc](https://godoc.org/github.com/blang/semver?status.png)](https://godoc.org/github.com/blang/semver) [![Coverage Status](https://img.shields.io/coveralls/blang/semver.svg)](https://coveralls.io/r/blang/semver?branch=master)
|
|
||||||
======
|
|
||||||
|
|
||||||
semver is a [Semantic Versioning](http://semver.org/) library written in golang. It fully covers spec version `2.0.0`.
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
```bash
|
|
||||||
$ go get github.com/blang/semver
|
|
||||||
```
|
|
||||||
Note: Always vendor your dependencies or fix on a specific version tag.
|
|
||||||
|
|
||||||
```go
|
|
||||||
import github.com/blang/semver
|
|
||||||
v1, err := semver.Make("1.0.0-beta")
|
|
||||||
v2, err := semver.Make("2.0.0-beta")
|
|
||||||
v1.Compare(v2)
|
|
||||||
```
|
|
||||||
|
|
||||||
Also check the [GoDocs](http://godoc.org/github.com/blang/semver).
|
|
||||||
|
|
||||||
Why should I use this lib?
|
|
||||||
-----
|
|
||||||
|
|
||||||
- Fully spec compatible
|
|
||||||
- No reflection
|
|
||||||
- No regex
|
|
||||||
- Fully tested (Coverage >99%)
|
|
||||||
- Readable parsing/validation errors
|
|
||||||
- Fast (See [Benchmarks](#benchmarks))
|
|
||||||
- Only Stdlib
|
|
||||||
- Uses values instead of pointers
|
|
||||||
- Many features, see below
|
|
||||||
|
|
||||||
|
|
||||||
Features
|
|
||||||
-----
|
|
||||||
|
|
||||||
- Parsing and validation at all levels
|
|
||||||
- Comparator-like comparisons
|
|
||||||
- Compare Helper Methods
|
|
||||||
- InPlace manipulation
|
|
||||||
- Ranges `>=1.0.0 <2.0.0 || >=3.0.0 !3.0.1-beta.1`
|
|
||||||
- Wildcards `>=1.x`, `<=2.5.x`
|
|
||||||
- Sortable (implements sort.Interface)
|
|
||||||
- database/sql compatible (sql.Scanner/Valuer)
|
|
||||||
- encoding/json compatible (json.Marshaler/Unmarshaler)
|
|
||||||
|
|
||||||
Ranges
|
|
||||||
------
|
|
||||||
|
|
||||||
A `Range` is a set of conditions which specify which versions satisfy the range.
|
|
||||||
|
|
||||||
A condition is composed of an operator and a version. The supported operators are:
|
|
||||||
|
|
||||||
- `<1.0.0` Less than `1.0.0`
|
|
||||||
- `<=1.0.0` Less than or equal to `1.0.0`
|
|
||||||
- `>1.0.0` Greater than `1.0.0`
|
|
||||||
- `>=1.0.0` Greater than or equal to `1.0.0`
|
|
||||||
- `1.0.0`, `=1.0.0`, `==1.0.0` Equal to `1.0.0`
|
|
||||||
- `!1.0.0`, `!=1.0.0` Not equal to `1.0.0`. Excludes version `1.0.0`.
|
|
||||||
|
|
||||||
Note that spaces between the operator and the version will be gracefully tolerated.
|
|
||||||
|
|
||||||
A `Range` can link multiple `Ranges` separated by space:
|
|
||||||
|
|
||||||
Ranges can be linked by logical AND:
|
|
||||||
|
|
||||||
- `>1.0.0 <2.0.0` would match between both ranges, so `1.1.1` and `1.8.7` but not `1.0.0` or `2.0.0`
|
|
||||||
- `>1.0.0 <3.0.0 !2.0.3-beta.2` would match every version between `1.0.0` and `3.0.0` except `2.0.3-beta.2`
|
|
||||||
|
|
||||||
Ranges can also be linked by logical OR:
|
|
||||||
|
|
||||||
- `<2.0.0 || >=3.0.0` would match `1.x.x` and `3.x.x` but not `2.x.x`
|
|
||||||
|
|
||||||
AND has a higher precedence than OR. It's not possible to use brackets.
|
|
||||||
|
|
||||||
Ranges can be combined by both AND and OR
|
|
||||||
|
|
||||||
- `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`
|
|
||||||
|
|
||||||
Range usage:
|
|
||||||
|
|
||||||
```
|
|
||||||
v, err := semver.Parse("1.2.3")
|
|
||||||
range, err := semver.ParseRange(">1.0.0 <2.0.0 || >=3.0.0")
|
|
||||||
if range(v) {
|
|
||||||
//valid
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Example
|
|
||||||
-----
|
|
||||||
|
|
||||||
Have a look at full examples in [examples/main.go](examples/main.go)
|
|
||||||
|
|
||||||
```go
|
|
||||||
import github.com/blang/semver
|
|
||||||
|
|
||||||
v, err := semver.Make("0.0.1-alpha.preview+123.github")
|
|
||||||
fmt.Printf("Major: %d\n", v.Major)
|
|
||||||
fmt.Printf("Minor: %d\n", v.Minor)
|
|
||||||
fmt.Printf("Patch: %d\n", v.Patch)
|
|
||||||
fmt.Printf("Pre: %s\n", v.Pre)
|
|
||||||
fmt.Printf("Build: %s\n", v.Build)
|
|
||||||
|
|
||||||
// Prerelease versions array
|
|
||||||
if len(v.Pre) > 0 {
|
|
||||||
fmt.Println("Prerelease versions:")
|
|
||||||
for i, pre := range v.Pre {
|
|
||||||
fmt.Printf("%d: %q\n", i, pre)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build meta data array
|
|
||||||
if len(v.Build) > 0 {
|
|
||||||
fmt.Println("Build meta data:")
|
|
||||||
for i, build := range v.Build {
|
|
||||||
fmt.Printf("%d: %q\n", i, build)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v001, err := semver.Make("0.0.1")
|
|
||||||
// Compare using helpers: v.GT(v2), v.LT, v.GTE, v.LTE
|
|
||||||
v001.GT(v) == true
|
|
||||||
v.LT(v001) == true
|
|
||||||
v.GTE(v) == true
|
|
||||||
v.LTE(v) == true
|
|
||||||
|
|
||||||
// Or use v.Compare(v2) for comparisons (-1, 0, 1):
|
|
||||||
v001.Compare(v) == 1
|
|
||||||
v.Compare(v001) == -1
|
|
||||||
v.Compare(v) == 0
|
|
||||||
|
|
||||||
// Manipulate Version in place:
|
|
||||||
v.Pre[0], err = semver.NewPRVersion("beta")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Error parsing pre release version: %q", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("\nValidate versions:")
|
|
||||||
v.Build[0] = "?"
|
|
||||||
|
|
||||||
err = v.Validate()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Validation failed: %s\n", err)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Benchmarks
|
|
||||||
-----
|
|
||||||
|
|
||||||
BenchmarkParseSimple-4 5000000 390 ns/op 48 B/op 1 allocs/op
|
|
||||||
BenchmarkParseComplex-4 1000000 1813 ns/op 256 B/op 7 allocs/op
|
|
||||||
BenchmarkParseAverage-4 1000000 1171 ns/op 163 B/op 4 allocs/op
|
|
||||||
BenchmarkStringSimple-4 20000000 119 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkStringLarger-4 10000000 206 ns/op 32 B/op 2 allocs/op
|
|
||||||
BenchmarkStringComplex-4 5000000 324 ns/op 80 B/op 3 allocs/op
|
|
||||||
BenchmarkStringAverage-4 5000000 273 ns/op 53 B/op 2 allocs/op
|
|
||||||
BenchmarkValidateSimple-4 200000000 9.33 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkValidateComplex-4 3000000 469 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkValidateAverage-4 5000000 256 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkCompareSimple-4 100000000 11.8 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkCompareComplex-4 50000000 30.8 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkCompareAverage-4 30000000 41.5 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkSort-4 3000000 419 ns/op 256 B/op 2 allocs/op
|
|
||||||
BenchmarkRangeParseSimple-4 2000000 850 ns/op 192 B/op 5 allocs/op
|
|
||||||
BenchmarkRangeParseAverage-4 1000000 1677 ns/op 400 B/op 10 allocs/op
|
|
||||||
BenchmarkRangeParseComplex-4 300000 5214 ns/op 1440 B/op 30 allocs/op
|
|
||||||
BenchmarkRangeMatchSimple-4 50000000 25.6 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkRangeMatchAverage-4 30000000 56.4 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkRangeMatchComplex-4 10000000 153 ns/op 0 B/op 0 allocs/op
|
|
||||||
|
|
||||||
See benchmark cases at [semver_test.go](semver_test.go)
|
|
||||||
|
|
||||||
|
|
||||||
Motivation
|
|
||||||
-----
|
|
||||||
|
|
||||||
I simply couldn't find any lib supporting the full spec. Others were just wrong or used reflection and regex which i don't like.
|
|
||||||
|
|
||||||
|
|
||||||
Contribution
|
|
||||||
-----
|
|
||||||
|
|
||||||
Feel free to make a pull request. For bigger changes create a issue first to discuss about it.
|
|
||||||
|
|
||||||
|
|
||||||
License
|
|
||||||
-----
|
|
||||||
|
|
||||||
See [LICENSE](LICENSE) file.
|
|
17
vendor/github.com/blang/semver/package.json
generated
vendored
17
vendor/github.com/blang/semver/package.json
generated
vendored
@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"author": "blang",
|
|
||||||
"bugs": {
|
|
||||||
"URL": "https://github.com/blang/semver/issues",
|
|
||||||
"url": "https://github.com/blang/semver/issues"
|
|
||||||
},
|
|
||||||
"gx": {
|
|
||||||
"dvcsimport": "github.com/blang/semver"
|
|
||||||
},
|
|
||||||
"gxVersion": "0.10.0",
|
|
||||||
"language": "go",
|
|
||||||
"license": "MIT",
|
|
||||||
"name": "semver",
|
|
||||||
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
|
|
||||||
"version": "3.5.1"
|
|
||||||
}
|
|
||||||
|
|
0
vendor/github.com/blang/semver/LICENSE → vendor/github.com/blang/semver/v4/LICENSE
generated
vendored
0
vendor/github.com/blang/semver/LICENSE → vendor/github.com/blang/semver/v4/LICENSE
generated
vendored
0
vendor/github.com/blang/semver/json.go → vendor/github.com/blang/semver/v4/json.go
generated
vendored
0
vendor/github.com/blang/semver/json.go → vendor/github.com/blang/semver/v4/json.go
generated
vendored
2
vendor/github.com/blang/semver/range.go → vendor/github.com/blang/semver/v4/range.go
generated
vendored
2
vendor/github.com/blang/semver/range.go → vendor/github.com/blang/semver/v4/range.go
generated
vendored
@ -327,7 +327,7 @@ func expandWildcardVersion(parts [][]string) ([][]string, error) {
|
|||||||
for _, p := range parts {
|
for _, p := range parts {
|
||||||
var newParts []string
|
var newParts []string
|
||||||
for _, ap := range p {
|
for _, ap := range p {
|
||||||
if strings.Index(ap, "x") != -1 {
|
if strings.Contains(ap, "x") {
|
||||||
opStr, vStr, err := splitComparatorVersion(ap)
|
opStr, vStr, err := splitComparatorVersion(ap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
72
vendor/github.com/blang/semver/semver.go → vendor/github.com/blang/semver/v4/semver.go
generated
vendored
72
vendor/github.com/blang/semver/semver.go → vendor/github.com/blang/semver/v4/semver.go
generated
vendored
@ -26,7 +26,7 @@ type Version struct {
|
|||||||
Minor uint64
|
Minor uint64
|
||||||
Patch uint64
|
Patch uint64
|
||||||
Pre []PRVersion
|
Pre []PRVersion
|
||||||
Build []string //No Precendence
|
Build []string //No Precedence
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version to string
|
// Version to string
|
||||||
@ -61,6 +61,18 @@ func (v Version) String() string {
|
|||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FinalizeVersion discards prerelease and build number and only returns
|
||||||
|
// major, minor and patch number.
|
||||||
|
func (v Version) FinalizeVersion() string {
|
||||||
|
b := make([]byte, 0, 5)
|
||||||
|
b = strconv.AppendUint(b, v.Major, 10)
|
||||||
|
b = append(b, '.')
|
||||||
|
b = strconv.AppendUint(b, v.Minor, 10)
|
||||||
|
b = append(b, '.')
|
||||||
|
b = strconv.AppendUint(b, v.Patch, 10)
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
// Equals checks if v is equal to o.
|
// Equals checks if v is equal to o.
|
||||||
func (v Version) Equals(o Version) bool {
|
func (v Version) Equals(o Version) bool {
|
||||||
return (v.Compare(o) == 0)
|
return (v.Compare(o) == 0)
|
||||||
@ -161,6 +173,27 @@ func (v Version) Compare(o Version) int {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IncrementPatch increments the patch version
|
||||||
|
func (v *Version) IncrementPatch() error {
|
||||||
|
v.Patch++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncrementMinor increments the minor version
|
||||||
|
func (v *Version) IncrementMinor() error {
|
||||||
|
v.Minor++
|
||||||
|
v.Patch = 0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncrementMajor increments the major version
|
||||||
|
func (v *Version) IncrementMajor() error {
|
||||||
|
v.Major++
|
||||||
|
v.Minor = 0
|
||||||
|
v.Patch = 0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Validate validates v and returns error in case
|
// Validate validates v and returns error in case
|
||||||
func (v Version) Validate() error {
|
func (v Version) Validate() error {
|
||||||
// Major, Minor, Patch already validated using uint64
|
// Major, Minor, Patch already validated using uint64
|
||||||
@ -189,10 +222,10 @@ func (v Version) Validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error
|
// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error
|
||||||
func New(s string) (vp *Version, err error) {
|
func New(s string) (*Version, error) {
|
||||||
v, err := Parse(s)
|
v, err := Parse(s)
|
||||||
vp = &v
|
vp := &v
|
||||||
return
|
return vp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make is an alias for Parse, parses version string and returns a validated Version or error
|
// Make is an alias for Parse, parses version string and returns a validated Version or error
|
||||||
@ -202,14 +235,25 @@ func Make(s string) (Version, error) {
|
|||||||
|
|
||||||
// ParseTolerant allows for certain version specifications that do not strictly adhere to semver
|
// ParseTolerant allows for certain version specifications that do not strictly adhere to semver
|
||||||
// specs to be parsed by this library. It does so by normalizing versions before passing them to
|
// specs to be parsed by this library. It does so by normalizing versions before passing them to
|
||||||
// Parse(). It currently trims spaces, removes a "v" prefix, and adds a 0 patch number to versions
|
// Parse(). It currently trims spaces, removes a "v" prefix, adds a 0 patch number to versions
|
||||||
// with only major and minor components specified
|
// with only major and minor components specified, and removes leading 0s.
|
||||||
func ParseTolerant(s string) (Version, error) {
|
func ParseTolerant(s string) (Version, error) {
|
||||||
s = strings.TrimSpace(s)
|
s = strings.TrimSpace(s)
|
||||||
s = strings.TrimPrefix(s, "v")
|
s = strings.TrimPrefix(s, "v")
|
||||||
|
|
||||||
// Split into major.minor.(patch+pr+meta)
|
// Split into major.minor.(patch+pr+meta)
|
||||||
parts := strings.SplitN(s, ".", 3)
|
parts := strings.SplitN(s, ".", 3)
|
||||||
|
// Remove leading zeros.
|
||||||
|
for i, p := range parts {
|
||||||
|
if len(p) > 1 {
|
||||||
|
p = strings.TrimLeft(p, "0")
|
||||||
|
if len(p) == 0 || !strings.ContainsAny(p[0:1], "0123456789") {
|
||||||
|
p = "0" + p
|
||||||
|
}
|
||||||
|
parts[i] = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fill up shortened versions.
|
||||||
if len(parts) < 3 {
|
if len(parts) < 3 {
|
||||||
if strings.ContainsAny(parts[len(parts)-1], "+-") {
|
if strings.ContainsAny(parts[len(parts)-1], "+-") {
|
||||||
return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data")
|
return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data")
|
||||||
@ -217,8 +261,8 @@ func ParseTolerant(s string) (Version, error) {
|
|||||||
for len(parts) < 3 {
|
for len(parts) < 3 {
|
||||||
parts = append(parts, "0")
|
parts = append(parts, "0")
|
||||||
}
|
}
|
||||||
s = strings.Join(parts, ".")
|
|
||||||
}
|
}
|
||||||
|
s = strings.Join(parts, ".")
|
||||||
|
|
||||||
return Parse(s)
|
return Parse(s)
|
||||||
}
|
}
|
||||||
@ -416,3 +460,17 @@ func NewBuildVersion(s string) (string, error) {
|
|||||||
}
|
}
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FinalizeVersion returns the major, minor and patch number only and discards
|
||||||
|
// prerelease and build number.
|
||||||
|
func FinalizeVersion(s string) (string, error) {
|
||||||
|
v, err := Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
v.Pre = nil
|
||||||
|
v.Build = nil
|
||||||
|
|
||||||
|
finalVer := v.String()
|
||||||
|
return finalVer, nil
|
||||||
|
}
|
0
vendor/github.com/blang/semver/sort.go → vendor/github.com/blang/semver/v4/sort.go
generated
vendored
0
vendor/github.com/blang/semver/sort.go → vendor/github.com/blang/semver/v4/sort.go
generated
vendored
2
vendor/github.com/blang/semver/sql.go → vendor/github.com/blang/semver/v4/sql.go
generated
vendored
2
vendor/github.com/blang/semver/sql.go → vendor/github.com/blang/semver/v4/sql.go
generated
vendored
@ -14,7 +14,7 @@ func (v *Version) Scan(src interface{}) (err error) {
|
|||||||
case []byte:
|
case []byte:
|
||||||
str = string(src)
|
str = string(src)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Version.Scan: cannot convert %T to string.", src)
|
return fmt.Errorf("version.Scan: cannot convert %T to string", src)
|
||||||
}
|
}
|
||||||
|
|
||||||
if t, err := Parse(str); err == nil {
|
if t, err := Parse(str); err == nil {
|
301
vendor/github.com/dyatlov/go-opengraph/opengraph/opengraph.go
generated
vendored
301
vendor/github.com/dyatlov/go-opengraph/opengraph/opengraph.go
generated
vendored
@ -8,81 +8,37 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/net/html"
|
"golang.org/x/net/html"
|
||||||
"golang.org/x/net/html/atom"
|
"golang.org/x/net/html/atom"
|
||||||
|
|
||||||
|
"github.com/dyatlov/go-opengraph/opengraph/types/actor"
|
||||||
|
"github.com/dyatlov/go-opengraph/opengraph/types/article"
|
||||||
|
"github.com/dyatlov/go-opengraph/opengraph/types/audio"
|
||||||
|
"github.com/dyatlov/go-opengraph/opengraph/types/book"
|
||||||
|
"github.com/dyatlov/go-opengraph/opengraph/types/image"
|
||||||
|
"github.com/dyatlov/go-opengraph/opengraph/types/music"
|
||||||
|
"github.com/dyatlov/go-opengraph/opengraph/types/profile"
|
||||||
|
"github.com/dyatlov/go-opengraph/opengraph/types/video"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Image defines Open Graph Image type
|
|
||||||
type Image struct {
|
|
||||||
URL string `json:"url"`
|
|
||||||
SecureURL string `json:"secure_url"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Width uint64 `json:"width"`
|
|
||||||
Height uint64 `json:"height"`
|
|
||||||
draft bool `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Video defines Open Graph Video type
|
|
||||||
type Video struct {
|
|
||||||
URL string `json:"url"`
|
|
||||||
SecureURL string `json:"secure_url"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Width uint64 `json:"width"`
|
|
||||||
Height uint64 `json:"height"`
|
|
||||||
draft bool `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Audio defines Open Graph Audio Type
|
|
||||||
type Audio struct {
|
|
||||||
URL string `json:"url"`
|
|
||||||
SecureURL string `json:"secure_url"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
draft bool `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Article contain Open Graph Article structure
|
|
||||||
type Article struct {
|
|
||||||
PublishedTime *time.Time `json:"published_time"`
|
|
||||||
ModifiedTime *time.Time `json:"modified_time"`
|
|
||||||
ExpirationTime *time.Time `json:"expiration_time"`
|
|
||||||
Section string `json:"section"`
|
|
||||||
Tags []string `json:"tags"`
|
|
||||||
Authors []*Profile `json:"authors"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Profile contains Open Graph Profile structure
|
|
||||||
type Profile struct {
|
|
||||||
FirstName string `json:"first_name"`
|
|
||||||
LastName string `json:"last_name"`
|
|
||||||
Username string `json:"username"`
|
|
||||||
Gender string `json:"gender"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Book contains Open Graph Book structure
|
|
||||||
type Book struct {
|
|
||||||
ISBN string `json:"isbn"`
|
|
||||||
ReleaseDate *time.Time `json:"release_date"`
|
|
||||||
Tags []string `json:"tags"`
|
|
||||||
Authors []*Profile `json:"authors"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenGraph contains facebook og data
|
// OpenGraph contains facebook og data
|
||||||
type OpenGraph struct {
|
type OpenGraph struct {
|
||||||
isArticle bool
|
isArticle bool
|
||||||
isBook bool
|
isBook bool
|
||||||
isProfile bool
|
isProfile bool
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Determiner string `json:"determiner"`
|
Determiner string `json:"determiner"`
|
||||||
SiteName string `json:"site_name"`
|
SiteName string `json:"site_name"`
|
||||||
Locale string `json:"locale"`
|
Locale string `json:"locale"`
|
||||||
LocalesAlternate []string `json:"locales_alternate"`
|
LocalesAlternate []string `json:"locales_alternate"`
|
||||||
Images []*Image `json:"images"`
|
Images []*image.Image `json:"images"`
|
||||||
Audios []*Audio `json:"audios"`
|
Audios []*audio.Audio `json:"audios"`
|
||||||
Videos []*Video `json:"videos"`
|
Videos []*video.Video `json:"videos"`
|
||||||
Article *Article `json:"article,omitempty"`
|
Article *article.Article `json:"article,omitempty"`
|
||||||
Book *Book `json:"book,omitempty"`
|
Book *book.Book `json:"book,omitempty"`
|
||||||
Profile *Profile `json:"profile,omitempty"`
|
Profile *profile.Profile `json:"profile,omitempty"`
|
||||||
|
Music *music.Music `json:"music,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOpenGraph returns new instance of Open Graph structure
|
// NewOpenGraph returns new instance of Open Graph structure
|
||||||
@ -137,21 +93,13 @@ func (og *OpenGraph) ensureHasVideo() {
|
|||||||
if len(og.Videos) > 0 {
|
if len(og.Videos) > 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
og.Videos = append(og.Videos, &Video{draft: true})
|
og.Videos = append(og.Videos, video.NewVideo())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (og *OpenGraph) ensureHasImage() {
|
func (og *OpenGraph) ensureHasMusic() {
|
||||||
if len(og.Images) > 0 {
|
if og.Music == nil {
|
||||||
return
|
og.Music = music.NewMusic()
|
||||||
}
|
}
|
||||||
og.Images = append(og.Images, &Image{draft: true})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (og *OpenGraph) ensureHasAudio() {
|
|
||||||
if len(og.Audios) > 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
og.Audios = append(og.Audios, &Audio{draft: true})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessMeta processes meta attributes and adds them to Open Graph structure if they are suitable for that
|
// ProcessMeta processes meta attributes and adds them to Open Graph structure if they are suitable for that
|
||||||
@ -182,73 +130,110 @@ func (og *OpenGraph) ProcessMeta(metaAttrs map[string]string) {
|
|||||||
case "og:locale:alternate":
|
case "og:locale:alternate":
|
||||||
og.LocalesAlternate = append(og.LocalesAlternate, metaAttrs["content"])
|
og.LocalesAlternate = append(og.LocalesAlternate, metaAttrs["content"])
|
||||||
case "og:audio":
|
case "og:audio":
|
||||||
if len(og.Audios)>0 && og.Audios[len(og.Audios)-1].draft {
|
og.Audios = audio.AddUrl(og.Audios, metaAttrs["content"])
|
||||||
og.Audios[len(og.Audios)-1].URL = metaAttrs["content"]
|
|
||||||
og.Audios[len(og.Audios)-1].draft = false
|
|
||||||
} else {
|
|
||||||
og.Audios = append(og.Audios, &Audio{URL: metaAttrs["content"]})
|
|
||||||
}
|
|
||||||
case "og:audio:secure_url":
|
case "og:audio:secure_url":
|
||||||
og.ensureHasAudio()
|
og.Audios = audio.AddSecureUrl(og.Audios, metaAttrs["content"])
|
||||||
og.Audios[len(og.Audios)-1].SecureURL = metaAttrs["content"]
|
|
||||||
case "og:audio:type":
|
case "og:audio:type":
|
||||||
og.ensureHasAudio()
|
og.Audios = audio.AddType(og.Audios, metaAttrs["content"])
|
||||||
og.Audios[len(og.Audios)-1].Type = metaAttrs["content"]
|
|
||||||
case "og:image":
|
case "og:image":
|
||||||
if len(og.Images)>0 && og.Images[len(og.Images)-1].draft {
|
og.Images = image.AddURL(og.Images, metaAttrs["content"])
|
||||||
og.Images[len(og.Images)-1].URL = metaAttrs["content"]
|
|
||||||
og.Images[len(og.Images)-1].draft = false
|
|
||||||
} else {
|
|
||||||
og.Images = append(og.Images, &Image{URL: metaAttrs["content"]})
|
|
||||||
}
|
|
||||||
case "og:image:url":
|
case "og:image:url":
|
||||||
og.ensureHasImage()
|
og.Images = image.AddURL(og.Images, metaAttrs["content"])
|
||||||
og.Images[len(og.Images)-1].URL = metaAttrs["content"]
|
|
||||||
case "og:image:secure_url":
|
case "og:image:secure_url":
|
||||||
og.ensureHasImage()
|
og.Images = image.AddSecureURL(og.Images, metaAttrs["content"])
|
||||||
og.Images[len(og.Images)-1].SecureURL = metaAttrs["content"]
|
|
||||||
case "og:image:type":
|
case "og:image:type":
|
||||||
og.ensureHasImage()
|
og.Images = image.AddType(og.Images, metaAttrs["content"])
|
||||||
og.Images[len(og.Images)-1].Type = metaAttrs["content"]
|
|
||||||
case "og:image:width":
|
case "og:image:width":
|
||||||
w, err := strconv.ParseUint(metaAttrs["content"], 10, 64)
|
w, err := strconv.ParseUint(metaAttrs["content"], 10, 64)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
og.ensureHasImage()
|
og.Images = image.AddWidth(og.Images, w)
|
||||||
og.Images[len(og.Images)-1].Width = w
|
|
||||||
}
|
}
|
||||||
case "og:image:height":
|
case "og:image:height":
|
||||||
h, err := strconv.ParseUint(metaAttrs["content"], 10, 64)
|
h, err := strconv.ParseUint(metaAttrs["content"], 10, 64)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
og.ensureHasImage()
|
og.Images = image.AddHeight(og.Images, h)
|
||||||
og.Images[len(og.Images)-1].Height = h
|
|
||||||
}
|
}
|
||||||
case "og:video":
|
case "og:video":
|
||||||
if len(og.Videos)>0 && og.Videos[len(og.Videos)-1].draft {
|
og.Videos = video.AddURL(og.Videos, metaAttrs["content"])
|
||||||
og.Videos[len(og.Videos)-1].URL = metaAttrs["content"]
|
case "og:video:tag":
|
||||||
og.Videos[len(og.Videos)-1].draft = false
|
og.Videos = video.AddTag(og.Videos, metaAttrs["content"])
|
||||||
} else {
|
case "og:video:duration":
|
||||||
og.Videos = append(og.Videos, &Video{URL: metaAttrs["content"]})
|
if i, err := strconv.ParseUint(metaAttrs["content"], 10, 64); err == nil {
|
||||||
|
og.Videos = video.AddDuration(og.Videos, i)
|
||||||
|
}
|
||||||
|
case "og:video:release_date":
|
||||||
|
if t, err := time.Parse(time.RFC3339, metaAttrs["content"]); err == nil {
|
||||||
|
og.Videos = video.AddReleaseDate(og.Videos, &t)
|
||||||
}
|
}
|
||||||
case "og:video:url":
|
case "og:video:url":
|
||||||
og.ensureHasVideo()
|
og.Videos = video.AddURL(og.Videos, metaAttrs["content"])
|
||||||
og.Videos[len(og.Videos)-1].URL = metaAttrs["content"]
|
|
||||||
case "og:video:secure_url":
|
case "og:video:secure_url":
|
||||||
og.ensureHasVideo()
|
og.Videos = video.AddSecureURL(og.Videos, metaAttrs["content"])
|
||||||
og.Videos[len(og.Videos)-1].SecureURL = metaAttrs["content"]
|
|
||||||
case "og:video:type":
|
case "og:video:type":
|
||||||
og.ensureHasVideo()
|
og.Videos = video.AddTag(og.Videos, metaAttrs["content"])
|
||||||
og.Videos[len(og.Videos)-1].Type = metaAttrs["content"]
|
|
||||||
case "og:video:width":
|
case "og:video:width":
|
||||||
w, err := strconv.ParseUint(metaAttrs["content"], 10, 64)
|
w, err := strconv.ParseUint(metaAttrs["content"], 10, 64)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
og.ensureHasVideo()
|
og.Videos = video.AddWidth(og.Videos, w)
|
||||||
og.Videos[len(og.Videos)-1].Width = w
|
|
||||||
}
|
}
|
||||||
case "og:video:height":
|
case "og:video:height":
|
||||||
h, err := strconv.ParseUint(metaAttrs["content"], 10, 64)
|
h, err := strconv.ParseUint(metaAttrs["content"], 10, 64)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
og.ensureHasVideo()
|
og.Videos = video.AddHeight(og.Videos, h)
|
||||||
og.Videos[len(og.Videos)-1].Height = h
|
}
|
||||||
|
case "og:video:actor":
|
||||||
|
og.ensureHasVideo()
|
||||||
|
og.Videos[len(og.Videos)-1].Actors = actor.AddProfile(og.Videos[len(og.Videos)-1].Actors, metaAttrs["content"])
|
||||||
|
case "og:video:actor:role":
|
||||||
|
og.ensureHasVideo()
|
||||||
|
og.Videos[len(og.Videos)-1].Actors = actor.AddRole(og.Videos[len(og.Videos)-1].Actors, metaAttrs["content"])
|
||||||
|
case "og:video:director":
|
||||||
|
og.ensureHasVideo()
|
||||||
|
og.Videos[len(og.Videos)-1].Directors = append(og.Videos[len(og.Videos)-1].Directors, metaAttrs["content"])
|
||||||
|
case "og:video:writer":
|
||||||
|
og.ensureHasVideo()
|
||||||
|
og.Videos[len(og.Videos)-1].Writers = append(og.Videos[len(og.Videos)-1].Writers, metaAttrs["content"])
|
||||||
|
case "og:music:duration":
|
||||||
|
og.ensureHasMusic()
|
||||||
|
if i, err := strconv.ParseUint(metaAttrs["content"], 10, 64); err == nil {
|
||||||
|
og.Music.Duration = i
|
||||||
|
}
|
||||||
|
case "og:music:release_date":
|
||||||
|
og.ensureHasMusic()
|
||||||
|
if t, err := time.Parse(time.RFC3339, metaAttrs["content"]); err == nil {
|
||||||
|
og.Music.ReleaseDate = &t
|
||||||
|
}
|
||||||
|
case "og:music:album":
|
||||||
|
og.ensureHasMusic()
|
||||||
|
og.Music.Album.URL = metaAttrs["content"]
|
||||||
|
case "og:music:album:disc":
|
||||||
|
og.ensureHasMusic()
|
||||||
|
if i, err := strconv.ParseUint(metaAttrs["content"], 10, 64); err == nil {
|
||||||
|
og.Music.Album.Disc = i
|
||||||
|
}
|
||||||
|
case "og:music:album:track":
|
||||||
|
og.ensureHasMusic()
|
||||||
|
if i, err := strconv.ParseUint(metaAttrs["content"], 10, 64); err == nil {
|
||||||
|
og.Music.Album.Track = i
|
||||||
|
}
|
||||||
|
case "og:music:musician":
|
||||||
|
og.ensureHasMusic()
|
||||||
|
og.Music.Musicians = append(og.Music.Musicians, metaAttrs["content"])
|
||||||
|
case "og:music:creator":
|
||||||
|
og.ensureHasMusic()
|
||||||
|
og.Music.Creators = append(og.Music.Creators, metaAttrs["content"])
|
||||||
|
case "og:music:song":
|
||||||
|
og.ensureHasMusic()
|
||||||
|
og.Music.AddSongUrl(metaAttrs["content"])
|
||||||
|
case "og:music:disc":
|
||||||
|
og.ensureHasMusic()
|
||||||
|
if i, err := strconv.ParseUint(metaAttrs["content"], 10, 64); err == nil {
|
||||||
|
og.Music.AddSongDisc(i)
|
||||||
|
}
|
||||||
|
case "og:music:track":
|
||||||
|
og.ensureHasMusic()
|
||||||
|
if i, err := strconv.ParseUint(metaAttrs["content"], 10, 64); err == nil {
|
||||||
|
og.Music.AddSongTrack(i)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if og.isArticle {
|
if og.isArticle {
|
||||||
@ -263,100 +248,64 @@ func (og *OpenGraph) ProcessMeta(metaAttrs map[string]string) {
|
|||||||
|
|
||||||
func (og *OpenGraph) processArticleMeta(metaAttrs map[string]string) {
|
func (og *OpenGraph) processArticleMeta(metaAttrs map[string]string) {
|
||||||
if og.Article == nil {
|
if og.Article == nil {
|
||||||
og.Article = &Article{}
|
og.Article = &article.Article{}
|
||||||
}
|
}
|
||||||
switch metaAttrs["property"] {
|
switch metaAttrs["property"] {
|
||||||
case "article:published_time":
|
case "og:article:published_time":
|
||||||
t, err := time.Parse(time.RFC3339, metaAttrs["content"])
|
t, err := time.Parse(time.RFC3339, metaAttrs["content"])
|
||||||
if err == nil {
|
if err == nil {
|
||||||
og.Article.PublishedTime = &t
|
og.Article.PublishedTime = &t
|
||||||
}
|
}
|
||||||
case "article:modified_time":
|
case "og:article:modified_time":
|
||||||
t, err := time.Parse(time.RFC3339, metaAttrs["content"])
|
t, err := time.Parse(time.RFC3339, metaAttrs["content"])
|
||||||
if err == nil {
|
if err == nil {
|
||||||
og.Article.ModifiedTime = &t
|
og.Article.ModifiedTime = &t
|
||||||
}
|
}
|
||||||
case "article:expiration_time":
|
case "og:article:expiration_time":
|
||||||
t, err := time.Parse(time.RFC3339, metaAttrs["content"])
|
t, err := time.Parse(time.RFC3339, metaAttrs["content"])
|
||||||
if err == nil {
|
if err == nil {
|
||||||
og.Article.ExpirationTime = &t
|
og.Article.ExpirationTime = &t
|
||||||
}
|
}
|
||||||
case "article:section":
|
case "og:article:section":
|
||||||
og.Article.Section = metaAttrs["content"]
|
og.Article.Section = metaAttrs["content"]
|
||||||
case "article:tag":
|
case "og:article:tag":
|
||||||
og.Article.Tags = append(og.Article.Tags, metaAttrs["content"])
|
og.Article.Tags = append(og.Article.Tags, metaAttrs["content"])
|
||||||
case "article:author:first_name":
|
case "og:article:author":
|
||||||
if len(og.Article.Authors) == 0 {
|
og.Article.Authors = append(og.Article.Authors, metaAttrs["content"])
|
||||||
og.Article.Authors = append(og.Article.Authors, &Profile{})
|
|
||||||
}
|
|
||||||
og.Article.Authors[len(og.Article.Authors)-1].FirstName = metaAttrs["content"]
|
|
||||||
case "article:author:last_name":
|
|
||||||
if len(og.Article.Authors) == 0 {
|
|
||||||
og.Article.Authors = append(og.Article.Authors, &Profile{})
|
|
||||||
}
|
|
||||||
og.Article.Authors[len(og.Article.Authors)-1].LastName = metaAttrs["content"]
|
|
||||||
case "article:author:username":
|
|
||||||
if len(og.Article.Authors) == 0 {
|
|
||||||
og.Article.Authors = append(og.Article.Authors, &Profile{})
|
|
||||||
}
|
|
||||||
og.Article.Authors[len(og.Article.Authors)-1].Username = metaAttrs["content"]
|
|
||||||
case "article:author:gender":
|
|
||||||
if len(og.Article.Authors) == 0 {
|
|
||||||
og.Article.Authors = append(og.Article.Authors, &Profile{})
|
|
||||||
}
|
|
||||||
og.Article.Authors[len(og.Article.Authors)-1].Gender = metaAttrs["content"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (og *OpenGraph) processBookMeta(metaAttrs map[string]string) {
|
func (og *OpenGraph) processBookMeta(metaAttrs map[string]string) {
|
||||||
if og.Book == nil {
|
if og.Book == nil {
|
||||||
og.Book = &Book{}
|
og.Book = &book.Book{}
|
||||||
}
|
}
|
||||||
switch metaAttrs["property"] {
|
switch metaAttrs["property"] {
|
||||||
case "book:release_date":
|
case "og:book:release_date":
|
||||||
t, err := time.Parse(time.RFC3339, metaAttrs["content"])
|
t, err := time.Parse(time.RFC3339, metaAttrs["content"])
|
||||||
if err == nil {
|
if err == nil {
|
||||||
og.Book.ReleaseDate = &t
|
og.Book.ReleaseDate = &t
|
||||||
}
|
}
|
||||||
case "book:isbn":
|
case "og:book:isbn":
|
||||||
og.Book.ISBN = metaAttrs["content"]
|
og.Book.ISBN = metaAttrs["content"]
|
||||||
case "book:tag":
|
case "og:book:tag":
|
||||||
og.Book.Tags = append(og.Book.Tags, metaAttrs["content"])
|
og.Book.Tags = append(og.Book.Tags, metaAttrs["content"])
|
||||||
case "book:author:first_name":
|
case "og:book:author":
|
||||||
if len(og.Book.Authors) == 0 {
|
og.Book.Authors = append(og.Book.Authors, metaAttrs["content"])
|
||||||
og.Book.Authors = append(og.Book.Authors, &Profile{})
|
|
||||||
}
|
|
||||||
og.Book.Authors[len(og.Book.Authors)-1].FirstName = metaAttrs["content"]
|
|
||||||
case "book:author:last_name":
|
|
||||||
if len(og.Book.Authors) == 0 {
|
|
||||||
og.Book.Authors = append(og.Book.Authors, &Profile{})
|
|
||||||
}
|
|
||||||
og.Book.Authors[len(og.Book.Authors)-1].LastName = metaAttrs["content"]
|
|
||||||
case "book:author:username":
|
|
||||||
if len(og.Book.Authors) == 0 {
|
|
||||||
og.Book.Authors = append(og.Book.Authors, &Profile{})
|
|
||||||
}
|
|
||||||
og.Book.Authors[len(og.Book.Authors)-1].Username = metaAttrs["content"]
|
|
||||||
case "book:author:gender":
|
|
||||||
if len(og.Book.Authors) == 0 {
|
|
||||||
og.Book.Authors = append(og.Book.Authors, &Profile{})
|
|
||||||
}
|
|
||||||
og.Book.Authors[len(og.Book.Authors)-1].Gender = metaAttrs["content"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (og *OpenGraph) processProfileMeta(metaAttrs map[string]string) {
|
func (og *OpenGraph) processProfileMeta(metaAttrs map[string]string) {
|
||||||
if og.Profile == nil {
|
if og.Profile == nil {
|
||||||
og.Profile = &Profile{}
|
og.Profile = &profile.Profile{}
|
||||||
}
|
}
|
||||||
switch metaAttrs["property"] {
|
switch metaAttrs["property"] {
|
||||||
case "profile:first_name":
|
case "og:profile:first_name":
|
||||||
og.Profile.FirstName = metaAttrs["content"]
|
og.Profile.FirstName = metaAttrs["content"]
|
||||||
case "profile:last_name":
|
case "og:profile:last_name":
|
||||||
og.Profile.LastName = metaAttrs["content"]
|
og.Profile.LastName = metaAttrs["content"]
|
||||||
case "profile:username":
|
case "og:profile:username":
|
||||||
og.Profile.Username = metaAttrs["content"]
|
og.Profile.Username = metaAttrs["content"]
|
||||||
case "profile:gender":
|
case "og:profile:gender":
|
||||||
og.Profile.Gender = metaAttrs["content"]
|
og.Profile.Gender = metaAttrs["content"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
27
vendor/github.com/dyatlov/go-opengraph/opengraph/types/actor/actor.go
generated
vendored
Normal file
27
vendor/github.com/dyatlov/go-opengraph/opengraph/types/actor/actor.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package actor
|
||||||
|
|
||||||
|
// Actor contain Open Graph Actor structure
|
||||||
|
type Actor struct {
|
||||||
|
Profile string `json:"profile"`
|
||||||
|
Role string `json:"role"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewActor() *Actor {
|
||||||
|
return &Actor{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddProfile(actors []*Actor, v string) []*Actor {
|
||||||
|
if len(actors) == 0 || actors[len(actors)-1].Profile != "" {
|
||||||
|
actors = append(actors, &Actor{})
|
||||||
|
}
|
||||||
|
actors[len(actors)-1].Profile = v
|
||||||
|
return actors
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddRole(actors []*Actor, v string) []*Actor {
|
||||||
|
if len(actors) == 0 || actors[len(actors)-1].Role != "" {
|
||||||
|
actors = append(actors, &Actor{})
|
||||||
|
}
|
||||||
|
actors[len(actors)-1].Role = v
|
||||||
|
return actors
|
||||||
|
}
|
15
vendor/github.com/dyatlov/go-opengraph/opengraph/types/article/article.go
generated
vendored
Normal file
15
vendor/github.com/dyatlov/go-opengraph/opengraph/types/article/article.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package article
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Article contain Open Graph Article structure
|
||||||
|
type Article struct {
|
||||||
|
PublishedTime *time.Time `json:"published_time"`
|
||||||
|
ModifiedTime *time.Time `json:"modified_time"`
|
||||||
|
ExpirationTime *time.Time `json:"expiration_time"`
|
||||||
|
Section string `json:"section"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
Authors []string `json:"authors"`
|
||||||
|
}
|
36
vendor/github.com/dyatlov/go-opengraph/opengraph/types/audio/audio.go
generated
vendored
Normal file
36
vendor/github.com/dyatlov/go-opengraph/opengraph/types/audio/audio.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package audio
|
||||||
|
|
||||||
|
// Audio defines Open Graph Audio Type
|
||||||
|
type Audio struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
SecureURL string `json:"secure_url"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAudio() *Audio {
|
||||||
|
return &Audio{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddUrl(audios []*Audio, v string) []*Audio {
|
||||||
|
if len(audios) == 0 || audios[len(audios)-1].URL != "" {
|
||||||
|
audios = append(audios, &Audio{})
|
||||||
|
}
|
||||||
|
audios[len(audios)-1].URL = v
|
||||||
|
return audios
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddSecureUrl(audios []*Audio, v string) []*Audio {
|
||||||
|
if len(audios) == 0 || audios[len(audios)-1].SecureURL != "" {
|
||||||
|
audios = append(audios, &Audio{})
|
||||||
|
}
|
||||||
|
audios[len(audios)-1].SecureURL = v
|
||||||
|
return audios
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddType(audios []*Audio, v string) []*Audio {
|
||||||
|
if len(audios) == 0 || audios[len(audios)-1].Type != "" {
|
||||||
|
audios = append(audios, &Audio{})
|
||||||
|
}
|
||||||
|
audios[len(audios)-1].Type = v
|
||||||
|
return audios
|
||||||
|
}
|
13
vendor/github.com/dyatlov/go-opengraph/opengraph/types/book/book.go
generated
vendored
Normal file
13
vendor/github.com/dyatlov/go-opengraph/opengraph/types/book/book.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package book
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Book contains Open Graph Book structure
|
||||||
|
type Book struct {
|
||||||
|
ISBN string `json:"isbn"`
|
||||||
|
ReleaseDate *time.Time `json:"release_date"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
Authors []string `json:"authors"`
|
||||||
|
}
|
53
vendor/github.com/dyatlov/go-opengraph/opengraph/types/image/image.go
generated
vendored
Normal file
53
vendor/github.com/dyatlov/go-opengraph/opengraph/types/image/image.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package image
|
||||||
|
|
||||||
|
// Image defines Open Graph Image type
|
||||||
|
type Image struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
SecureURL string `json:"secure_url"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Width uint64 `json:"width"`
|
||||||
|
Height uint64 `json:"height"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewImage() *Image {
|
||||||
|
return &Image{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureHasImage(images []*Image) []*Image {
|
||||||
|
if len(images) == 0 {
|
||||||
|
images = append(images, NewImage())
|
||||||
|
}
|
||||||
|
return images
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddURL(images []*Image, v string) []*Image {
|
||||||
|
if len(images) == 0 || (images[len(images)-1].URL != "" && images[len(images)-1].URL != v) {
|
||||||
|
images = append(images, NewImage())
|
||||||
|
}
|
||||||
|
images[len(images)-1].URL = v
|
||||||
|
return images
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddSecureURL(images []*Image, v string) []*Image {
|
||||||
|
images = ensureHasImage(images)
|
||||||
|
images[len(images)-1].SecureURL = v
|
||||||
|
return images
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddType(images []*Image, v string) []*Image {
|
||||||
|
images = ensureHasImage(images)
|
||||||
|
images[len(images)-1].Type = v
|
||||||
|
return images
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddWidth(images []*Image, v uint64) []*Image {
|
||||||
|
images = ensureHasImage(images)
|
||||||
|
images[len(images)-1].Width = v
|
||||||
|
return images
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddHeight(images []*Image, v uint64) []*Image {
|
||||||
|
images = ensureHasImage(images)
|
||||||
|
images[len(images)-1].Height = v
|
||||||
|
return images
|
||||||
|
}
|
52
vendor/github.com/dyatlov/go-opengraph/opengraph/types/music/music.go
generated
vendored
Normal file
52
vendor/github.com/dyatlov/go-opengraph/opengraph/types/music/music.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package music
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Music defines Open Graph Music type
|
||||||
|
type Music struct {
|
||||||
|
Musicians []string `json:"musicians,omitempty"`
|
||||||
|
Creators []string `json:"creators,omitempty"`
|
||||||
|
Duration uint64 `json:"duration,omitempty"`
|
||||||
|
ReleaseDate *time.Time `json:"release_date,omitempty"`
|
||||||
|
Album *Album `json:"album"`
|
||||||
|
Songs []*Song `json:"songs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Album struct {
|
||||||
|
URL string `json:"url,omitempty"`
|
||||||
|
Disc uint64 `json:"disc,omitempty"`
|
||||||
|
Track uint64 `json:"track,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Song struct {
|
||||||
|
URL string `json:"url,omitempty"`
|
||||||
|
Disc uint64 `json:"disc,omitempty"`
|
||||||
|
Track uint64 `json:"track,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMusic() *Music {
|
||||||
|
return &Music{Album: &Album{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Music) AddSongUrl(v string) {
|
||||||
|
if len(m.Songs) == 0 || m.Songs[len(m.Songs)-1].URL != "" {
|
||||||
|
m.Songs = append(m.Songs, &Song{})
|
||||||
|
}
|
||||||
|
m.Songs[len(m.Songs)-1].URL = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Music) AddSongDisc(v uint64) {
|
||||||
|
if len(m.Songs) == 0 {
|
||||||
|
m.Songs = append(m.Songs, &Song{})
|
||||||
|
}
|
||||||
|
m.Songs[len(m.Songs)-1].Disc = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Music) AddSongTrack(v uint64) {
|
||||||
|
if len(m.Songs) == 0 {
|
||||||
|
m.Songs = append(m.Songs, &Song{})
|
||||||
|
}
|
||||||
|
m.Songs[len(m.Songs)-1].Track = v
|
||||||
|
}
|
59
vendor/github.com/dyatlov/go-opengraph/opengraph/types/profile/profile.go
generated
vendored
Normal file
59
vendor/github.com/dyatlov/go-opengraph/opengraph/types/profile/profile.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package profile
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// Profile contain Open Graph Profile structure
|
||||||
|
type Profile struct {
|
||||||
|
FirstName string `json:"first_name"`
|
||||||
|
LastName string `json:"last_name"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Gender string `json:"gender"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProfile() *Profile {
|
||||||
|
return &Profile{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddBasicProfile(profiles []*Profile, v string) []*Profile {
|
||||||
|
parts := strings.SplitN(v, " ", 2)
|
||||||
|
if len(profiles) == 0 || profiles[len(profiles)-1].FirstName != "" {
|
||||||
|
profiles = append(profiles, &Profile{})
|
||||||
|
}
|
||||||
|
profiles[len(profiles)-1].FirstName = parts[0]
|
||||||
|
if len(parts) > 1 {
|
||||||
|
profiles[len(profiles)-1].LastName = parts[1]
|
||||||
|
}
|
||||||
|
return profiles
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddFirstName(profiles []*Profile, v string) []*Profile {
|
||||||
|
if len(profiles) == 0 || profiles[len(profiles)-1].FirstName != "" {
|
||||||
|
profiles = append(profiles, &Profile{})
|
||||||
|
}
|
||||||
|
profiles[len(profiles)-1].FirstName = v
|
||||||
|
return profiles
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddLastName(profiles []*Profile, v string) []*Profile {
|
||||||
|
if len(profiles) == 0 || profiles[len(profiles)-1].LastName != "" {
|
||||||
|
profiles = append(profiles, &Profile{})
|
||||||
|
}
|
||||||
|
profiles[len(profiles)-1].LastName = v
|
||||||
|
return profiles
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddUsername(profiles []*Profile, v string) []*Profile {
|
||||||
|
if len(profiles) == 0 || profiles[len(profiles)-1].Username != "" {
|
||||||
|
profiles = append(profiles, &Profile{})
|
||||||
|
}
|
||||||
|
profiles[len(profiles)-1].Username = v
|
||||||
|
return profiles
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddGender(profiles []*Profile, v string) []*Profile {
|
||||||
|
if len(profiles) == 0 || profiles[len(profiles)-1].Gender != "" {
|
||||||
|
profiles = append(profiles, &Profile{})
|
||||||
|
}
|
||||||
|
profiles[len(profiles)-1].Gender = v
|
||||||
|
return profiles
|
||||||
|
}
|
83
vendor/github.com/dyatlov/go-opengraph/opengraph/types/video/video.go
generated
vendored
Normal file
83
vendor/github.com/dyatlov/go-opengraph/opengraph/types/video/video.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package video
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/dyatlov/go-opengraph/opengraph/types/actor"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Video defines Open Graph Video type
|
||||||
|
type Video struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
SecureURL string `json:"secure_url"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Width uint64 `json:"width"`
|
||||||
|
Height uint64 `json:"height"`
|
||||||
|
Actors []*actor.Actor `json:"actors,omitempty"`
|
||||||
|
Directors []string `json:"directors,omitempty"`
|
||||||
|
Writers []string `json:"writers,omitempty"`
|
||||||
|
Duration uint64 `json:"duration,omitempty"`
|
||||||
|
ReleaseDate *time.Time `json:"release_date,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVideo() *Video {
|
||||||
|
return &Video{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureHasVideo(videos []*Video) []*Video {
|
||||||
|
if len(videos) == 0 {
|
||||||
|
videos = append(videos, NewVideo())
|
||||||
|
}
|
||||||
|
return videos
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddURL(videos []*Video, v string) []*Video {
|
||||||
|
if len(videos) == 0 || (videos[len(videos)-1].URL != "" && videos[len(videos)-1].URL != v) {
|
||||||
|
videos = append(videos, NewVideo())
|
||||||
|
}
|
||||||
|
videos[len(videos)-1].URL = v
|
||||||
|
return videos
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddTag(videos []*Video, v string) []*Video {
|
||||||
|
videos = ensureHasVideo(videos)
|
||||||
|
videos[len(videos)-1].Tags = append(videos[len(videos)-1].Tags, v)
|
||||||
|
return videos
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddDuration(videos []*Video, v uint64) []*Video {
|
||||||
|
videos = ensureHasVideo(videos)
|
||||||
|
videos[len(videos)-1].Duration = v
|
||||||
|
return videos
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddReleaseDate(videos []*Video, v *time.Time) []*Video {
|
||||||
|
videos = ensureHasVideo(videos)
|
||||||
|
videos[len(videos)-1].ReleaseDate = v
|
||||||
|
return videos
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddSecureURL(videos []*Video, v string) []*Video {
|
||||||
|
videos = ensureHasVideo(videos)
|
||||||
|
videos[len(videos)-1].SecureURL = v
|
||||||
|
return videos
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddType(videos []*Video, v string) []*Video {
|
||||||
|
videos = ensureHasVideo(videos)
|
||||||
|
videos[len(videos)-1].Type = v
|
||||||
|
return videos
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddWidth(videos []*Video, v uint64) []*Video {
|
||||||
|
videos = ensureHasVideo(videos)
|
||||||
|
videos[len(videos)-1].Width = v
|
||||||
|
return videos
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddHeight(videos []*Video, v uint64) []*Video {
|
||||||
|
videos = ensureHasVideo(videos)
|
||||||
|
videos[len(videos)-1].Height = v
|
||||||
|
return videos
|
||||||
|
}
|
20
vendor/github.com/fatih/color/LICENSE.md
generated
vendored
Normal file
20
vendor/github.com/fatih/color/LICENSE.md
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 Fatih Arslan
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
176
vendor/github.com/fatih/color/README.md
generated
vendored
Normal file
176
vendor/github.com/fatih/color/README.md
generated
vendored
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
# color [![](https://github.com/fatih/color/workflows/build/badge.svg)](https://github.com/fatih/color/actions) [![PkgGoDev](https://pkg.go.dev/badge/github.com/fatih/color)](https://pkg.go.dev/github.com/fatih/color)
|
||||||
|
|
||||||
|
Color lets you use colorized outputs in terms of [ANSI Escape
|
||||||
|
Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It
|
||||||
|
has support for Windows too! The API can be used in several ways, pick one that
|
||||||
|
suits you.
|
||||||
|
|
||||||
|
![Color](https://user-images.githubusercontent.com/438920/96832689-03b3e000-13f4-11eb-9803-46f4c4de3406.jpg)
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get github.com/fatih/color
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Standard colors
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Print with default helper functions
|
||||||
|
color.Cyan("Prints text in cyan.")
|
||||||
|
|
||||||
|
// A newline will be appended automatically
|
||||||
|
color.Blue("Prints %s in blue.", "text")
|
||||||
|
|
||||||
|
// These are using the default foreground colors
|
||||||
|
color.Red("We have red")
|
||||||
|
color.Magenta("And many others ..")
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mix and reuse colors
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Create a new color object
|
||||||
|
c := color.New(color.FgCyan).Add(color.Underline)
|
||||||
|
c.Println("Prints cyan text with an underline.")
|
||||||
|
|
||||||
|
// Or just add them to New()
|
||||||
|
d := color.New(color.FgCyan, color.Bold)
|
||||||
|
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||||
|
|
||||||
|
// Mix up foreground and background colors, create new mixes!
|
||||||
|
red := color.New(color.FgRed)
|
||||||
|
|
||||||
|
boldRed := red.Add(color.Bold)
|
||||||
|
boldRed.Println("This will print text in bold red.")
|
||||||
|
|
||||||
|
whiteBackground := red.Add(color.BgWhite)
|
||||||
|
whiteBackground.Println("Red text with white background.")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use your own output (io.Writer)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Use your own io.Writer output
|
||||||
|
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
|
||||||
|
|
||||||
|
blue := color.New(color.FgBlue)
|
||||||
|
blue.Fprint(writer, "This will print text in blue.")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom print functions (PrintFunc)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Create a custom print function for convenience
|
||||||
|
red := color.New(color.FgRed).PrintfFunc()
|
||||||
|
red("Warning")
|
||||||
|
red("Error: %s", err)
|
||||||
|
|
||||||
|
// Mix up multiple attributes
|
||||||
|
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||||
|
notice("Don't forget this...")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom fprint functions (FprintFunc)
|
||||||
|
|
||||||
|
```go
|
||||||
|
blue := color.New(color.FgBlue).FprintfFunc()
|
||||||
|
blue(myWriter, "important notice: %s", stars)
|
||||||
|
|
||||||
|
// Mix up with multiple attributes
|
||||||
|
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
|
||||||
|
success(myWriter, "Don't forget this...")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Insert into noncolor strings (SprintFunc)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Create SprintXxx functions to mix strings with other non-colorized strings:
|
||||||
|
yellow := color.New(color.FgYellow).SprintFunc()
|
||||||
|
red := color.New(color.FgRed).SprintFunc()
|
||||||
|
fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||||
|
|
||||||
|
info := color.New(color.FgWhite, color.BgGreen).SprintFunc()
|
||||||
|
fmt.Printf("This %s rocks!\n", info("package"))
|
||||||
|
|
||||||
|
// Use helper functions
|
||||||
|
fmt.Println("This", color.RedString("warning"), "should be not neglected.")
|
||||||
|
fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.")
|
||||||
|
|
||||||
|
// Windows supported too! Just don't forget to change the output to color.Output
|
||||||
|
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Plug into existing code
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Use handy standard colors
|
||||||
|
color.Set(color.FgYellow)
|
||||||
|
|
||||||
|
fmt.Println("Existing text will now be in yellow")
|
||||||
|
fmt.Printf("This one %s\n", "too")
|
||||||
|
|
||||||
|
color.Unset() // Don't forget to unset
|
||||||
|
|
||||||
|
// You can mix up parameters
|
||||||
|
color.Set(color.FgMagenta, color.Bold)
|
||||||
|
defer color.Unset() // Use it in your function
|
||||||
|
|
||||||
|
fmt.Println("All text will now be bold magenta.")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disable/Enable color
|
||||||
|
|
||||||
|
There might be a case where you want to explicitly disable/enable color output. the
|
||||||
|
`go-isatty` package will automatically disable color output for non-tty output streams
|
||||||
|
(for example if the output were piped directly to `less`).
|
||||||
|
|
||||||
|
The `color` package also disables color output if the [`NO_COLOR`](https://no-color.org) environment
|
||||||
|
variable is set to a non-empty string.
|
||||||
|
|
||||||
|
`Color` has support to disable/enable colors programmatically both globally and
|
||||||
|
for single color definitions. For example suppose you have a CLI app and a
|
||||||
|
`-no-color` bool flag. You can easily disable the color output with:
|
||||||
|
|
||||||
|
```go
|
||||||
|
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||||
|
|
||||||
|
if *flagNoColor {
|
||||||
|
color.NoColor = true // disables colorized output
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It also has support for single color definitions (local). You can
|
||||||
|
disable/enable color output on the fly:
|
||||||
|
|
||||||
|
```go
|
||||||
|
c := color.New(color.FgCyan)
|
||||||
|
c.Println("Prints cyan text")
|
||||||
|
|
||||||
|
c.DisableColor()
|
||||||
|
c.Println("This is printed without any color")
|
||||||
|
|
||||||
|
c.EnableColor()
|
||||||
|
c.Println("This prints again cyan...")
|
||||||
|
```
|
||||||
|
|
||||||
|
## GitHub Actions
|
||||||
|
|
||||||
|
To output color in GitHub Actions (or other CI systems that support ANSI colors), make sure to set `color.NoColor = false` so that it bypasses the check for non-tty output streams.
|
||||||
|
|
||||||
|
## Todo
|
||||||
|
|
||||||
|
* Save/Return previous values
|
||||||
|
* Evaluate fmt.Formatter interface
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
* [Fatih Arslan](https://github.com/fatih)
|
||||||
|
* Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details
|
650
vendor/github.com/fatih/color/color.go
generated
vendored
Normal file
650
vendor/github.com/fatih/color/color.go
generated
vendored
Normal file
@ -0,0 +1,650 @@
|
|||||||
|
package color
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/mattn/go-colorable"
|
||||||
|
"github.com/mattn/go-isatty"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// NoColor defines if the output is colorized or not. It's dynamically set to
|
||||||
|
// false or true based on the stdout's file descriptor referring to a terminal
|
||||||
|
// or not. It's also set to true if the NO_COLOR environment variable is
|
||||||
|
// set (regardless of its value). This is a global option and affects all
|
||||||
|
// colors. For more control over each color block use the methods
|
||||||
|
// DisableColor() individually.
|
||||||
|
NoColor = noColorIsSet() || os.Getenv("TERM") == "dumb" ||
|
||||||
|
(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
|
||||||
|
|
||||||
|
// Output defines the standard output of the print functions. By default,
|
||||||
|
// os.Stdout is used.
|
||||||
|
Output = colorable.NewColorableStdout()
|
||||||
|
|
||||||
|
// Error defines a color supporting writer for os.Stderr.
|
||||||
|
Error = colorable.NewColorableStderr()
|
||||||
|
|
||||||
|
// colorsCache is used to reduce the count of created Color objects and
|
||||||
|
// allows to reuse already created objects with required Attribute.
|
||||||
|
colorsCache = make(map[Attribute]*Color)
|
||||||
|
colorsCacheMu sync.Mutex // protects colorsCache
|
||||||
|
)
|
||||||
|
|
||||||
|
// noColorIsSet returns true if the environment variable NO_COLOR is set to a non-empty string.
|
||||||
|
func noColorIsSet() bool {
|
||||||
|
return os.Getenv("NO_COLOR") != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color defines a custom color object which is defined by SGR parameters.
|
||||||
|
type Color struct {
|
||||||
|
params []Attribute
|
||||||
|
noColor *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attribute defines a single SGR Code
|
||||||
|
type Attribute int
|
||||||
|
|
||||||
|
const escape = "\x1b"
|
||||||
|
|
||||||
|
// Base attributes
|
||||||
|
const (
|
||||||
|
Reset Attribute = iota
|
||||||
|
Bold
|
||||||
|
Faint
|
||||||
|
Italic
|
||||||
|
Underline
|
||||||
|
BlinkSlow
|
||||||
|
BlinkRapid
|
||||||
|
ReverseVideo
|
||||||
|
Concealed
|
||||||
|
CrossedOut
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ResetBold Attribute = iota + 22
|
||||||
|
ResetItalic
|
||||||
|
ResetUnderline
|
||||||
|
ResetBlinking
|
||||||
|
_
|
||||||
|
ResetReversed
|
||||||
|
ResetConcealed
|
||||||
|
ResetCrossedOut
|
||||||
|
)
|
||||||
|
|
||||||
|
var mapResetAttributes map[Attribute]Attribute = map[Attribute]Attribute{
|
||||||
|
Bold: ResetBold,
|
||||||
|
Faint: ResetBold,
|
||||||
|
Italic: ResetItalic,
|
||||||
|
Underline: ResetUnderline,
|
||||||
|
BlinkSlow: ResetBlinking,
|
||||||
|
BlinkRapid: ResetBlinking,
|
||||||
|
ReverseVideo: ResetReversed,
|
||||||
|
Concealed: ResetConcealed,
|
||||||
|
CrossedOut: ResetCrossedOut,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Foreground text colors
|
||||||
|
const (
|
||||||
|
FgBlack Attribute = iota + 30
|
||||||
|
FgRed
|
||||||
|
FgGreen
|
||||||
|
FgYellow
|
||||||
|
FgBlue
|
||||||
|
FgMagenta
|
||||||
|
FgCyan
|
||||||
|
FgWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// Foreground Hi-Intensity text colors
|
||||||
|
const (
|
||||||
|
FgHiBlack Attribute = iota + 90
|
||||||
|
FgHiRed
|
||||||
|
FgHiGreen
|
||||||
|
FgHiYellow
|
||||||
|
FgHiBlue
|
||||||
|
FgHiMagenta
|
||||||
|
FgHiCyan
|
||||||
|
FgHiWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// Background text colors
|
||||||
|
const (
|
||||||
|
BgBlack Attribute = iota + 40
|
||||||
|
BgRed
|
||||||
|
BgGreen
|
||||||
|
BgYellow
|
||||||
|
BgBlue
|
||||||
|
BgMagenta
|
||||||
|
BgCyan
|
||||||
|
BgWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// Background Hi-Intensity text colors
|
||||||
|
const (
|
||||||
|
BgHiBlack Attribute = iota + 100
|
||||||
|
BgHiRed
|
||||||
|
BgHiGreen
|
||||||
|
BgHiYellow
|
||||||
|
BgHiBlue
|
||||||
|
BgHiMagenta
|
||||||
|
BgHiCyan
|
||||||
|
BgHiWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// New returns a newly created color object.
|
||||||
|
func New(value ...Attribute) *Color {
|
||||||
|
c := &Color{
|
||||||
|
params: make([]Attribute, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
if noColorIsSet() {
|
||||||
|
c.noColor = boolPtr(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Add(value...)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the given parameters immediately. It will change the color of
|
||||||
|
// output with the given SGR parameters until color.Unset() is called.
|
||||||
|
func Set(p ...Attribute) *Color {
|
||||||
|
c := New(p...)
|
||||||
|
c.Set()
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset resets all escape attributes and clears the output. Usually should
|
||||||
|
// be called after Set().
|
||||||
|
func Unset() {
|
||||||
|
if NoColor {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(Output, "%s[%dm", escape, Reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the SGR sequence.
|
||||||
|
func (c *Color) Set() *Color {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprint(Output, c.format())
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) unset() {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Unset()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriter is used to set the SGR sequence with the given io.Writer. This is
|
||||||
|
// a low-level function, and users should use the higher-level functions, such
|
||||||
|
// as color.Fprint, color.Print, etc.
|
||||||
|
func (c *Color) SetWriter(w io.Writer) *Color {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprint(w, c.format())
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsetWriter resets all escape attributes and clears the output with the give
|
||||||
|
// io.Writer. Usually should be called after SetWriter().
|
||||||
|
func (c *Color) UnsetWriter(w io.Writer) {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if NoColor {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(w, "%s[%dm", escape, Reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add is used to chain SGR parameters. Use as many as parameters to combine
|
||||||
|
// and create custom color objects. Example: Add(color.FgRed, color.Underline).
|
||||||
|
func (c *Color) Add(value ...Attribute) *Color {
|
||||||
|
c.params = append(c.params, value...)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprint formats using the default formats for its operands and writes to w.
|
||||||
|
// Spaces are added between operands when neither is a string.
|
||||||
|
// It returns the number of bytes written and any write error encountered.
|
||||||
|
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||||
|
// type *os.File.
|
||||||
|
func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||||
|
c.SetWriter(w)
|
||||||
|
defer c.UnsetWriter(w)
|
||||||
|
|
||||||
|
return fmt.Fprint(w, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print formats using the default formats for its operands and writes to
|
||||||
|
// standard output. Spaces are added between operands when neither is a
|
||||||
|
// string. It returns the number of bytes written and any write error
|
||||||
|
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||||
|
// color.
|
||||||
|
func (c *Color) Print(a ...interface{}) (n int, err error) {
|
||||||
|
c.Set()
|
||||||
|
defer c.unset()
|
||||||
|
|
||||||
|
return fmt.Fprint(Output, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprintf formats according to a format specifier and writes to w.
|
||||||
|
// It returns the number of bytes written and any write error encountered.
|
||||||
|
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||||
|
// type *os.File.
|
||||||
|
func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||||
|
c.SetWriter(w)
|
||||||
|
defer c.UnsetWriter(w)
|
||||||
|
|
||||||
|
return fmt.Fprintf(w, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf formats according to a format specifier and writes to standard output.
|
||||||
|
// It returns the number of bytes written and any write error encountered.
|
||||||
|
// This is the standard fmt.Printf() method wrapped with the given color.
|
||||||
|
func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
|
||||||
|
c.Set()
|
||||||
|
defer c.unset()
|
||||||
|
|
||||||
|
return fmt.Fprintf(Output, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprintln formats using the default formats for its operands and writes to w.
|
||||||
|
// Spaces are always added between operands and a newline is appended.
|
||||||
|
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||||
|
// type *os.File.
|
||||||
|
func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprintln(w, c.wrap(fmt.Sprint(a...)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println formats using the default formats for its operands and writes to
|
||||||
|
// standard output. Spaces are always added between operands and a newline is
|
||||||
|
// appended. It returns the number of bytes written and any write error
|
||||||
|
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||||
|
// color.
|
||||||
|
func (c *Color) Println(a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprintln(Output, c.wrap(fmt.Sprint(a...)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprint is just like Print, but returns a string instead of printing it.
|
||||||
|
func (c *Color) Sprint(a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprint(a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintln is just like Println, but returns a string instead of printing it.
|
||||||
|
func (c *Color) Sprintln(a ...interface{}) string {
|
||||||
|
return fmt.Sprintln(c.Sprint(a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintf is just like Printf, but returns a string instead of printing it.
|
||||||
|
func (c *Color) Sprintf(format string, a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FprintFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Fprint().
|
||||||
|
func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) {
|
||||||
|
return func(w io.Writer, a ...interface{}) {
|
||||||
|
c.Fprint(w, a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Print().
|
||||||
|
func (c *Color) PrintFunc() func(a ...interface{}) {
|
||||||
|
return func(a ...interface{}) {
|
||||||
|
c.Print(a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FprintfFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Fprintf().
|
||||||
|
func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) {
|
||||||
|
return func(w io.Writer, format string, a ...interface{}) {
|
||||||
|
c.Fprintf(w, format, a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintfFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Printf().
|
||||||
|
func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
|
||||||
|
return func(format string, a ...interface{}) {
|
||||||
|
c.Printf(format, a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FprintlnFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Fprintln().
|
||||||
|
func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) {
|
||||||
|
return func(w io.Writer, a ...interface{}) {
|
||||||
|
c.Fprintln(w, a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintlnFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Println().
|
||||||
|
func (c *Color) PrintlnFunc() func(a ...interface{}) {
|
||||||
|
return func(a ...interface{}) {
|
||||||
|
c.Println(a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SprintFunc returns a new function that returns colorized strings for the
|
||||||
|
// given arguments with fmt.Sprint(). Useful to put into or mix into other
|
||||||
|
// string. Windows users should use this in conjunction with color.Output, example:
|
||||||
|
//
|
||||||
|
// put := New(FgYellow).SprintFunc()
|
||||||
|
// fmt.Fprintf(color.Output, "This is a %s", put("warning"))
|
||||||
|
func (c *Color) SprintFunc() func(a ...interface{}) string {
|
||||||
|
return func(a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprint(a...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SprintfFunc returns a new function that returns colorized strings for the
|
||||||
|
// given arguments with fmt.Sprintf(). Useful to put into or mix into other
|
||||||
|
// string. Windows users should use this in conjunction with color.Output.
|
||||||
|
func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
|
||||||
|
return func(format string, a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SprintlnFunc returns a new function that returns colorized strings for the
|
||||||
|
// given arguments with fmt.Sprintln(). Useful to put into or mix into other
|
||||||
|
// string. Windows users should use this in conjunction with color.Output.
|
||||||
|
func (c *Color) SprintlnFunc() func(a ...interface{}) string {
|
||||||
|
return func(a ...interface{}) string {
|
||||||
|
return fmt.Sprintln(c.Sprint(a...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m"
|
||||||
|
// an example output might be: "1;36" -> bold cyan
|
||||||
|
func (c *Color) sequence() string {
|
||||||
|
format := make([]string, len(c.params))
|
||||||
|
for i, v := range c.params {
|
||||||
|
format[i] = strconv.Itoa(int(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(format, ";")
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrap wraps the s string with the colors attributes. The string is ready to
|
||||||
|
// be printed.
|
||||||
|
func (c *Color) wrap(s string) string {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.format() + s + c.unformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) format() string {
|
||||||
|
return fmt.Sprintf("%s[%sm", escape, c.sequence())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) unformat() string {
|
||||||
|
//return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||||
|
//for each element in sequence let's use the speficic reset escape, ou the generic one if not found
|
||||||
|
format := make([]string, len(c.params))
|
||||||
|
for i, v := range c.params {
|
||||||
|
format[i] = strconv.Itoa(int(Reset))
|
||||||
|
ra, ok := mapResetAttributes[v]
|
||||||
|
if ok {
|
||||||
|
format[i] = strconv.Itoa(int(ra))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s[%sm", escape, strings.Join(format, ";"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableColor disables the color output. Useful to not change any existing
|
||||||
|
// code and still being able to output. Can be used for flags like
|
||||||
|
// "--no-color". To enable back use EnableColor() method.
|
||||||
|
func (c *Color) DisableColor() {
|
||||||
|
c.noColor = boolPtr(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableColor enables the color output. Use it in conjunction with
|
||||||
|
// DisableColor(). Otherwise, this method has no side effects.
|
||||||
|
func (c *Color) EnableColor() {
|
||||||
|
c.noColor = boolPtr(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) isNoColorSet() bool {
|
||||||
|
// check first if we have user set action
|
||||||
|
if c.noColor != nil {
|
||||||
|
return *c.noColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not return the global option, which is disabled by default
|
||||||
|
return NoColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals returns a boolean value indicating whether two colors are equal.
|
||||||
|
func (c *Color) Equals(c2 *Color) bool {
|
||||||
|
if c == nil && c2 == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if c == nil || c2 == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(c.params) != len(c2.params) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, attr := range c.params {
|
||||||
|
if !c2.attrExists(attr) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) attrExists(a Attribute) bool {
|
||||||
|
for _, attr := range c.params {
|
||||||
|
if attr == a {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolPtr(v bool) *bool {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCachedColor(p Attribute) *Color {
|
||||||
|
colorsCacheMu.Lock()
|
||||||
|
defer colorsCacheMu.Unlock()
|
||||||
|
|
||||||
|
c, ok := colorsCache[p]
|
||||||
|
if !ok {
|
||||||
|
c = New(p)
|
||||||
|
colorsCache[p] = c
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func colorPrint(format string, p Attribute, a ...interface{}) {
|
||||||
|
c := getCachedColor(p)
|
||||||
|
|
||||||
|
if !strings.HasSuffix(format, "\n") {
|
||||||
|
format += "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(a) == 0 {
|
||||||
|
c.Print(format)
|
||||||
|
} else {
|
||||||
|
c.Printf(format, a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func colorString(format string, p Attribute, a ...interface{}) string {
|
||||||
|
c := getCachedColor(p)
|
||||||
|
|
||||||
|
if len(a) == 0 {
|
||||||
|
return c.SprintFunc()(format)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SprintfFunc()(format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Black is a convenient helper function to print with black foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) }
|
||||||
|
|
||||||
|
// Red is a convenient helper function to print with red foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) }
|
||||||
|
|
||||||
|
// Green is a convenient helper function to print with green foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) }
|
||||||
|
|
||||||
|
// Yellow is a convenient helper function to print with yellow foreground.
|
||||||
|
// A newline is appended to format by default.
|
||||||
|
func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) }
|
||||||
|
|
||||||
|
// Blue is a convenient helper function to print with blue foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) }
|
||||||
|
|
||||||
|
// Magenta is a convenient helper function to print with magenta foreground.
|
||||||
|
// A newline is appended to format by default.
|
||||||
|
func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) }
|
||||||
|
|
||||||
|
// Cyan is a convenient helper function to print with cyan foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) }
|
||||||
|
|
||||||
|
// White is a convenient helper function to print with white foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) }
|
||||||
|
|
||||||
|
// BlackString is a convenient helper function to return a string with black
|
||||||
|
// foreground.
|
||||||
|
func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
|
||||||
|
|
||||||
|
// RedString is a convenient helper function to return a string with red
|
||||||
|
// foreground.
|
||||||
|
func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
|
||||||
|
|
||||||
|
// GreenString is a convenient helper function to return a string with green
|
||||||
|
// foreground.
|
||||||
|
func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
|
||||||
|
|
||||||
|
// YellowString is a convenient helper function to return a string with yellow
|
||||||
|
// foreground.
|
||||||
|
func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
|
||||||
|
|
||||||
|
// BlueString is a convenient helper function to return a string with blue
|
||||||
|
// foreground.
|
||||||
|
func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
|
||||||
|
|
||||||
|
// MagentaString is a convenient helper function to return a string with magenta
|
||||||
|
// foreground.
|
||||||
|
func MagentaString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgMagenta, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CyanString is a convenient helper function to return a string with cyan
|
||||||
|
// foreground.
|
||||||
|
func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
|
||||||
|
|
||||||
|
// WhiteString is a convenient helper function to return a string with white
|
||||||
|
// foreground.
|
||||||
|
func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
|
||||||
|
|
||||||
|
// HiBlack is a convenient helper function to print with hi-intensity black foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) }
|
||||||
|
|
||||||
|
// HiRed is a convenient helper function to print with hi-intensity red foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) }
|
||||||
|
|
||||||
|
// HiGreen is a convenient helper function to print with hi-intensity green foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) }
|
||||||
|
|
||||||
|
// HiYellow is a convenient helper function to print with hi-intensity yellow foreground.
|
||||||
|
// A newline is appended to format by default.
|
||||||
|
func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) }
|
||||||
|
|
||||||
|
// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) }
|
||||||
|
|
||||||
|
// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground.
|
||||||
|
// A newline is appended to format by default.
|
||||||
|
func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) }
|
||||||
|
|
||||||
|
// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) }
|
||||||
|
|
||||||
|
// HiWhite is a convenient helper function to print with hi-intensity white foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) }
|
||||||
|
|
||||||
|
// HiBlackString is a convenient helper function to return a string with hi-intensity black
|
||||||
|
// foreground.
|
||||||
|
func HiBlackString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiBlack, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HiRedString is a convenient helper function to return a string with hi-intensity red
|
||||||
|
// foreground.
|
||||||
|
func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) }
|
||||||
|
|
||||||
|
// HiGreenString is a convenient helper function to return a string with hi-intensity green
|
||||||
|
// foreground.
|
||||||
|
func HiGreenString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiGreen, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HiYellowString is a convenient helper function to return a string with hi-intensity yellow
|
||||||
|
// foreground.
|
||||||
|
func HiYellowString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiYellow, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HiBlueString is a convenient helper function to return a string with hi-intensity blue
|
||||||
|
// foreground.
|
||||||
|
func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) }
|
||||||
|
|
||||||
|
// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta
|
||||||
|
// foreground.
|
||||||
|
func HiMagentaString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiMagenta, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HiCyanString is a convenient helper function to return a string with hi-intensity cyan
|
||||||
|
// foreground.
|
||||||
|
func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) }
|
||||||
|
|
||||||
|
// HiWhiteString is a convenient helper function to return a string with hi-intensity white
|
||||||
|
// foreground.
|
||||||
|
func HiWhiteString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiWhite, a...)
|
||||||
|
}
|
19
vendor/github.com/fatih/color/color_windows.go
generated
vendored
Normal file
19
vendor/github.com/fatih/color/color_windows.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package color
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Opt-in for ansi color support for current process.
|
||||||
|
// https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#output-sequences
|
||||||
|
var outMode uint32
|
||||||
|
out := windows.Handle(os.Stdout.Fd())
|
||||||
|
if err := windows.GetConsoleMode(out, &outMode); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
outMode |= windows.ENABLE_PROCESSED_OUTPUT | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||||
|
_ = windows.SetConsoleMode(out, outMode)
|
||||||
|
}
|
134
vendor/github.com/fatih/color/doc.go
generated
vendored
Normal file
134
vendor/github.com/fatih/color/doc.go
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
Package color is an ANSI color package to output colorized or SGR defined
|
||||||
|
output to the standard output. The API can be used in several way, pick one
|
||||||
|
that suits you.
|
||||||
|
|
||||||
|
Use simple and default helper functions with predefined foreground colors:
|
||||||
|
|
||||||
|
color.Cyan("Prints text in cyan.")
|
||||||
|
|
||||||
|
// a newline will be appended automatically
|
||||||
|
color.Blue("Prints %s in blue.", "text")
|
||||||
|
|
||||||
|
// More default foreground colors..
|
||||||
|
color.Red("We have red")
|
||||||
|
color.Yellow("Yellow color too!")
|
||||||
|
color.Magenta("And many others ..")
|
||||||
|
|
||||||
|
// Hi-intensity colors
|
||||||
|
color.HiGreen("Bright green color.")
|
||||||
|
color.HiBlack("Bright black means gray..")
|
||||||
|
color.HiWhite("Shiny white color!")
|
||||||
|
|
||||||
|
However, there are times when custom color mixes are required. Below are some
|
||||||
|
examples to create custom color objects and use the print functions of each
|
||||||
|
separate color object.
|
||||||
|
|
||||||
|
// Create a new color object
|
||||||
|
c := color.New(color.FgCyan).Add(color.Underline)
|
||||||
|
c.Println("Prints cyan text with an underline.")
|
||||||
|
|
||||||
|
// Or just add them to New()
|
||||||
|
d := color.New(color.FgCyan, color.Bold)
|
||||||
|
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||||
|
|
||||||
|
|
||||||
|
// Mix up foreground and background colors, create new mixes!
|
||||||
|
red := color.New(color.FgRed)
|
||||||
|
|
||||||
|
boldRed := red.Add(color.Bold)
|
||||||
|
boldRed.Println("This will print text in bold red.")
|
||||||
|
|
||||||
|
whiteBackground := red.Add(color.BgWhite)
|
||||||
|
whiteBackground.Println("Red text with White background.")
|
||||||
|
|
||||||
|
// Use your own io.Writer output
|
||||||
|
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
|
||||||
|
|
||||||
|
blue := color.New(color.FgBlue)
|
||||||
|
blue.Fprint(myWriter, "This will print text in blue.")
|
||||||
|
|
||||||
|
You can create PrintXxx functions to simplify even more:
|
||||||
|
|
||||||
|
// Create a custom print function for convenient
|
||||||
|
red := color.New(color.FgRed).PrintfFunc()
|
||||||
|
red("warning")
|
||||||
|
red("error: %s", err)
|
||||||
|
|
||||||
|
// Mix up multiple attributes
|
||||||
|
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||||
|
notice("don't forget this...")
|
||||||
|
|
||||||
|
You can also FprintXxx functions to pass your own io.Writer:
|
||||||
|
|
||||||
|
blue := color.New(FgBlue).FprintfFunc()
|
||||||
|
blue(myWriter, "important notice: %s", stars)
|
||||||
|
|
||||||
|
// Mix up with multiple attributes
|
||||||
|
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
|
||||||
|
success(myWriter, don't forget this...")
|
||||||
|
|
||||||
|
Or create SprintXxx functions to mix strings with other non-colorized strings:
|
||||||
|
|
||||||
|
yellow := New(FgYellow).SprintFunc()
|
||||||
|
red := New(FgRed).SprintFunc()
|
||||||
|
|
||||||
|
fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||||
|
|
||||||
|
info := New(FgWhite, BgGreen).SprintFunc()
|
||||||
|
fmt.Printf("this %s rocks!\n", info("package"))
|
||||||
|
|
||||||
|
Windows support is enabled by default. All Print functions work as intended.
|
||||||
|
However, only for color.SprintXXX functions, user should use fmt.FprintXXX and
|
||||||
|
set the output to color.Output:
|
||||||
|
|
||||||
|
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||||
|
|
||||||
|
info := New(FgWhite, BgGreen).SprintFunc()
|
||||||
|
fmt.Fprintf(color.Output, "this %s rocks!\n", info("package"))
|
||||||
|
|
||||||
|
Using with existing code is possible. Just use the Set() method to set the
|
||||||
|
standard output to the given parameters. That way a rewrite of an existing
|
||||||
|
code is not required.
|
||||||
|
|
||||||
|
// Use handy standard colors.
|
||||||
|
color.Set(color.FgYellow)
|
||||||
|
|
||||||
|
fmt.Println("Existing text will be now in Yellow")
|
||||||
|
fmt.Printf("This one %s\n", "too")
|
||||||
|
|
||||||
|
color.Unset() // don't forget to unset
|
||||||
|
|
||||||
|
// You can mix up parameters
|
||||||
|
color.Set(color.FgMagenta, color.Bold)
|
||||||
|
defer color.Unset() // use it in your function
|
||||||
|
|
||||||
|
fmt.Println("All text will be now bold magenta.")
|
||||||
|
|
||||||
|
There might be a case where you want to disable color output (for example to
|
||||||
|
pipe the standard output of your app to somewhere else). `Color` has support to
|
||||||
|
disable colors both globally and for single color definition. For example
|
||||||
|
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable
|
||||||
|
the color output with:
|
||||||
|
|
||||||
|
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||||
|
|
||||||
|
if *flagNoColor {
|
||||||
|
color.NoColor = true // disables colorized output
|
||||||
|
}
|
||||||
|
|
||||||
|
You can also disable the color by setting the NO_COLOR environment variable to any value.
|
||||||
|
|
||||||
|
It also has support for single color definitions (local). You can
|
||||||
|
disable/enable color output on the fly:
|
||||||
|
|
||||||
|
c := color.New(color.FgCyan)
|
||||||
|
c.Println("Prints cyan text")
|
||||||
|
|
||||||
|
c.DisableColor()
|
||||||
|
c.Println("This is printed without any color")
|
||||||
|
|
||||||
|
c.EnableColor()
|
||||||
|
c.Println("This prints again cyan...")
|
||||||
|
*/
|
||||||
|
package color
|
43
vendor/github.com/go-asn1-ber/asn1-ber/.travis.yml
generated
vendored
43
vendor/github.com/go-asn1-ber/asn1-ber/.travis.yml
generated
vendored
@ -1,43 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.2.x
|
|
||||||
- 1.6.x
|
|
||||||
- 1.9.x
|
|
||||||
- 1.10.x
|
|
||||||
- 1.11.x
|
|
||||||
- 1.12.x
|
|
||||||
- 1.14.x
|
|
||||||
- tip
|
|
||||||
|
|
||||||
os:
|
|
||||||
- linux
|
|
||||||
|
|
||||||
arch:
|
|
||||||
- amd64
|
|
||||||
- ppc64le
|
|
||||||
|
|
||||||
dist: xenial
|
|
||||||
|
|
||||||
env:
|
|
||||||
- GOARCH=amd64
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
include:
|
|
||||||
- os: windows
|
|
||||||
go: 1.14.x
|
|
||||||
- os: osx
|
|
||||||
go: 1.14.x
|
|
||||||
- os: linux
|
|
||||||
go: 1.14.x
|
|
||||||
arch: arm64
|
|
||||||
- os: linux
|
|
||||||
go: 1.14.x
|
|
||||||
env:
|
|
||||||
- GOARCH=386
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go test -v -cover ./... || go test -v ./...
|
|
||||||
matrix:
|
|
||||||
allowfailures:
|
|
||||||
go: 1.2.x
|
|
29
vendor/github.com/go-asn1-ber/asn1-ber/ber.go
generated
vendored
29
vendor/github.com/go-asn1-ber/asn1-ber/ber.go
generated
vendored
@ -170,12 +170,10 @@ func PrintPacket(p *Packet) {
|
|||||||
printPacket(os.Stdout, p, 0, false)
|
printPacket(os.Stdout, p, 0, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
|
// Return a string describing packet content. This is not recursive,
|
||||||
indentStr := ""
|
// If the packet is a sequence, use `printPacket()`, or browse
|
||||||
|
// sequence yourself.
|
||||||
for len(indentStr) != indent {
|
func DescribePacket(p *Packet) string {
|
||||||
indentStr += " "
|
|
||||||
}
|
|
||||||
|
|
||||||
classStr := ClassMap[p.ClassType]
|
classStr := ClassMap[p.ClassType]
|
||||||
|
|
||||||
@ -194,7 +192,17 @@ func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
|
|||||||
description = p.Description + ": "
|
description = p.Description + ": "
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _ = fmt.Fprintf(out, "%s%s(%s, %s, %s) Len=%d %q\n", indentStr, description, classStr, tagTypeStr, tagStr, p.Data.Len(), value)
|
return fmt.Sprintf("%s(%s, %s, %s) Len=%d %q", description, classStr, tagTypeStr, tagStr, p.Data.Len(), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
|
||||||
|
indentStr := ""
|
||||||
|
|
||||||
|
for len(indentStr) != indent {
|
||||||
|
indentStr += " "
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = fmt.Fprintf(out, "%s%s\n", indentStr, DescribePacket(p))
|
||||||
|
|
||||||
if printBytes {
|
if printBytes {
|
||||||
PrintBytes(out, p.Bytes(), indentStr)
|
PrintBytes(out, p.Bytes(), indentStr)
|
||||||
@ -317,7 +325,7 @@ func readPacket(reader io.Reader) (*Packet, int, error) {
|
|||||||
// Read the next packet
|
// Read the next packet
|
||||||
child, r, err := readPacket(reader)
|
child, r, err := readPacket(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, read, err
|
return nil, read, unexpectedEOF(err)
|
||||||
}
|
}
|
||||||
contentRead += r
|
contentRead += r
|
||||||
read += r
|
read += r
|
||||||
@ -348,10 +356,7 @@ func readPacket(reader io.Reader) (*Packet, int, error) {
|
|||||||
if length > 0 {
|
if length > 0 {
|
||||||
_, err := io.ReadFull(reader, content)
|
_, err := io.ReadFull(reader, content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
return nil, read, unexpectedEOF(err)
|
||||||
return nil, read, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return nil, read, err
|
|
||||||
}
|
}
|
||||||
read += length
|
read += length
|
||||||
}
|
}
|
||||||
|
2
vendor/github.com/go-asn1-ber/asn1-ber/identifier.go
generated
vendored
2
vendor/github.com/go-asn1-ber/asn1-ber/identifier.go
generated
vendored
@ -37,7 +37,7 @@ func readIdentifier(reader io.Reader) (Identifier, int, error) {
|
|||||||
if Debug {
|
if Debug {
|
||||||
fmt.Printf("error reading high-tag-number tag byte %d: %v\n", tagBytes, err)
|
fmt.Printf("error reading high-tag-number tag byte %d: %v\n", tagBytes, err)
|
||||||
}
|
}
|
||||||
return Identifier{}, read, err
|
return Identifier{}, read, unexpectedEOF(err)
|
||||||
}
|
}
|
||||||
tagBytes++
|
tagBytes++
|
||||||
read++
|
read++
|
||||||
|
4
vendor/github.com/go-asn1-ber/asn1-ber/length.go
generated
vendored
4
vendor/github.com/go-asn1-ber/asn1-ber/length.go
generated
vendored
@ -13,7 +13,7 @@ func readLength(reader io.Reader) (length int, read int, err error) {
|
|||||||
if Debug {
|
if Debug {
|
||||||
fmt.Printf("error reading length byte: %v\n", err)
|
fmt.Printf("error reading length byte: %v\n", err)
|
||||||
}
|
}
|
||||||
return 0, 0, err
|
return 0, 0, unexpectedEOF(err)
|
||||||
}
|
}
|
||||||
read++
|
read++
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ func readLength(reader io.Reader) (length int, read int, err error) {
|
|||||||
if Debug {
|
if Debug {
|
||||||
fmt.Printf("error reading long-form length byte %d: %v\n", i, err)
|
fmt.Printf("error reading long-form length byte %d: %v\n", i, err)
|
||||||
}
|
}
|
||||||
return 0, read, err
|
return 0, read, unexpectedEOF(err)
|
||||||
}
|
}
|
||||||
read++
|
read++
|
||||||
|
|
||||||
|
6
vendor/github.com/go-asn1-ber/asn1-ber/real.go
generated
vendored
6
vendor/github.com/go-asn1-ber/asn1-ber/real.go
generated
vendored
@ -89,12 +89,18 @@ func parseBinaryFloat(v []byte) (float64, error) {
|
|||||||
case 0x02:
|
case 0x02:
|
||||||
expLen = 3
|
expLen = 3
|
||||||
case 0x03:
|
case 0x03:
|
||||||
|
if len(v) < 2 {
|
||||||
|
return 0.0, errors.New("invalid data")
|
||||||
|
}
|
||||||
expLen = int(v[0])
|
expLen = int(v[0])
|
||||||
if expLen > 8 {
|
if expLen > 8 {
|
||||||
return 0.0, errors.New("too big value of exponent")
|
return 0.0, errors.New("too big value of exponent")
|
||||||
}
|
}
|
||||||
v = v[1:]
|
v = v[1:]
|
||||||
}
|
}
|
||||||
|
if expLen > len(v) {
|
||||||
|
return 0.0, errors.New("too big value of exponent")
|
||||||
|
}
|
||||||
buf, v = v[:expLen], v[expLen:]
|
buf, v = v[:expLen], v[expLen:]
|
||||||
exponent, err := ParseInt64(buf)
|
exponent, err := ParseInt64(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
10
vendor/github.com/go-asn1-ber/asn1-ber/util.go
generated
vendored
10
vendor/github.com/go-asn1-ber/asn1-ber/util.go
generated
vendored
@ -6,14 +6,18 @@ func readByte(reader io.Reader) (byte, error) {
|
|||||||
bytes := make([]byte, 1)
|
bytes := make([]byte, 1)
|
||||||
_, err := io.ReadFull(reader, bytes)
|
_, err := io.ReadFull(reader, bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
return bytes[0], nil
|
return bytes[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unexpectedEOF(err error) error {
|
||||||
|
if err == io.EOF {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func isEOCPacket(p *Packet) bool {
|
func isEOCPacket(p *Packet) bool {
|
||||||
return p != nil &&
|
return p != nil &&
|
||||||
p.Tag == TagEOC &&
|
p.Tag == TagEOC &&
|
||||||
|
62
vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go
generated
vendored
Normal file
62
vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// source: github.com/golang/protobuf/ptypes/empty/empty.proto
|
||||||
|
|
||||||
|
package empty
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Symbols defined in public import of google/protobuf/empty.proto.
|
||||||
|
|
||||||
|
type Empty = emptypb.Empty
|
||||||
|
|
||||||
|
var File_github_com_golang_protobuf_ptypes_empty_empty_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_github_com_golang_protobuf_ptypes_empty_empty_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
|
||||||
|
0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
|
||||||
|
0x70, 0x65, 0x73, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e,
|
||||||
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,
|
||||||
|
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||||
|
0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||||
|
0x2f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x3b, 0x65, 0x6d,
|
||||||
|
0x70, 0x74, 0x79, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_github_com_golang_protobuf_ptypes_empty_empty_proto_goTypes = []interface{}{}
|
||||||
|
var file_github_com_golang_protobuf_ptypes_empty_empty_proto_depIdxs = []int32{
|
||||||
|
0, // [0:0] is the sub-list for method output_type
|
||||||
|
0, // [0:0] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_github_com_golang_protobuf_ptypes_empty_empty_proto_init() }
|
||||||
|
func file_github_com_golang_protobuf_ptypes_empty_empty_proto_init() {
|
||||||
|
if File_github_com_golang_protobuf_ptypes_empty_empty_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_github_com_golang_protobuf_ptypes_empty_empty_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 0,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_github_com_golang_protobuf_ptypes_empty_empty_proto_goTypes,
|
||||||
|
DependencyIndexes: file_github_com_golang_protobuf_ptypes_empty_empty_proto_depIdxs,
|
||||||
|
}.Build()
|
||||||
|
File_github_com_golang_protobuf_ptypes_empty_empty_proto = out.File
|
||||||
|
file_github_com_golang_protobuf_ptypes_empty_empty_proto_rawDesc = nil
|
||||||
|
file_github_com_golang_protobuf_ptypes_empty_empty_proto_goTypes = nil
|
||||||
|
file_github_com_golang_protobuf_ptypes_empty_empty_proto_depIdxs = nil
|
||||||
|
}
|
20
vendor/github.com/gorilla/websocket/.editorconfig
generated
vendored
Normal file
20
vendor/github.com/gorilla/websocket/.editorconfig
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
; https://editorconfig.org/
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
insert_final_newline = true
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[{Makefile,go.mod,go.sum,*.go,.gitmodules}]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
indent_size = 4
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
eclint_indent_style = unset
|
26
vendor/github.com/gorilla/websocket/.gitignore
generated
vendored
26
vendor/github.com/gorilla/websocket/.gitignore
generated
vendored
@ -1,25 +1 @@
|
|||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
coverage.coverprofile
|
||||||
*.o
|
|
||||||
*.a
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Folders
|
|
||||||
_obj
|
|
||||||
_test
|
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
|
||||||
*.[568vq]
|
|
||||||
[568vq].out
|
|
||||||
|
|
||||||
*.cgo1.go
|
|
||||||
*.cgo2.c
|
|
||||||
_cgo_defun.c
|
|
||||||
_cgo_gotypes.go
|
|
||||||
_cgo_export.*
|
|
||||||
|
|
||||||
_testmain.go
|
|
||||||
|
|
||||||
*.exe
|
|
||||||
|
|
||||||
.idea/
|
|
||||||
*.iml
|
|
||||||
|
3
vendor/github.com/gorilla/websocket/.golangci.yml
generated
vendored
Normal file
3
vendor/github.com/gorilla/websocket/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
run:
|
||||||
|
skip-dirs:
|
||||||
|
- examples/*.go
|
9
vendor/github.com/gorilla/websocket/AUTHORS
generated
vendored
9
vendor/github.com/gorilla/websocket/AUTHORS
generated
vendored
@ -1,9 +0,0 @@
|
|||||||
# This is the official list of Gorilla WebSocket authors for copyright
|
|
||||||
# purposes.
|
|
||||||
#
|
|
||||||
# Please keep the list sorted.
|
|
||||||
|
|
||||||
Gary Burd <gary@beagledreams.com>
|
|
||||||
Google LLC (https://opensource.google.com/)
|
|
||||||
Joachim Bauch <mail@joachim-bauch.de>
|
|
||||||
|
|
39
vendor/github.com/gorilla/websocket/LICENSE
generated
vendored
39
vendor/github.com/gorilla/websocket/LICENSE
generated
vendored
@ -1,22 +1,27 @@
|
|||||||
Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.
|
Copyright (c) 2023 The Gorilla Authors. All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are met:
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
Redistributions of source code must retain the above copyright notice, this
|
* Redistributions of source code must retain the above copyright
|
||||||
list of conditions and the following disclaimer.
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
Redistributions in binary form must reproduce the above copyright notice,
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
this list of conditions and the following disclaimer in the documentation
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
and/or other materials provided with the distribution.
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
34
vendor/github.com/gorilla/websocket/Makefile
generated
vendored
Normal file
34
vendor/github.com/gorilla/websocket/Makefile
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
GO_LINT=$(shell which golangci-lint 2> /dev/null || echo '')
|
||||||
|
GO_LINT_URI=github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||||
|
|
||||||
|
GO_SEC=$(shell which gosec 2> /dev/null || echo '')
|
||||||
|
GO_SEC_URI=github.com/securego/gosec/v2/cmd/gosec@latest
|
||||||
|
|
||||||
|
GO_VULNCHECK=$(shell which govulncheck 2> /dev/null || echo '')
|
||||||
|
GO_VULNCHECK_URI=golang.org/x/vuln/cmd/govulncheck@latest
|
||||||
|
|
||||||
|
.PHONY: golangci-lint
|
||||||
|
golangci-lint:
|
||||||
|
$(if $(GO_LINT), ,go install $(GO_LINT_URI))
|
||||||
|
@echo "##### Running golangci-lint"
|
||||||
|
golangci-lint run -v
|
||||||
|
|
||||||
|
.PHONY: gosec
|
||||||
|
gosec:
|
||||||
|
$(if $(GO_SEC), ,go install $(GO_SEC_URI))
|
||||||
|
@echo "##### Running gosec"
|
||||||
|
gosec -exclude-dir examples ./...
|
||||||
|
|
||||||
|
.PHONY: govulncheck
|
||||||
|
govulncheck:
|
||||||
|
$(if $(GO_VULNCHECK), ,go install $(GO_VULNCHECK_URI))
|
||||||
|
@echo "##### Running govulncheck"
|
||||||
|
govulncheck ./...
|
||||||
|
|
||||||
|
.PHONY: verify
|
||||||
|
verify: golangci-lint gosec govulncheck
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
@echo "##### Running tests"
|
||||||
|
go test -race -cover -coverprofile=coverage.coverprofile -covermode=atomic -v ./...
|
19
vendor/github.com/gorilla/websocket/README.md
generated
vendored
19
vendor/github.com/gorilla/websocket/README.md
generated
vendored
@ -1,17 +1,14 @@
|
|||||||
# Gorilla WebSocket
|
# gorilla/websocket
|
||||||
|
|
||||||
[![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket)
|
![testing](https://github.com/gorilla/websocket/actions/workflows/test.yml/badge.svg)
|
||||||
[![CircleCI](https://circleci.com/gh/gorilla/websocket.svg?style=svg)](https://circleci.com/gh/gorilla/websocket)
|
[![codecov](https://codecov.io/github/gorilla/websocket/branch/main/graph/badge.svg)](https://codecov.io/github/gorilla/websocket)
|
||||||
|
[![godoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket)
|
||||||
|
[![sourcegraph](https://sourcegraph.com/github.com/gorilla/websocket/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/websocket?badge)
|
||||||
|
|
||||||
Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
|
Gorilla WebSocket is a [Go](http://golang.org/) implementation of the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
|
||||||
[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
|
|
||||||
|
|
||||||
|
![Gorilla Logo](https://github.com/gorilla/.github/assets/53367916/d92caabf-98e0-473e-bfbf-ab554ba435e5)
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
⚠️ **[The Gorilla WebSocket Package is looking for a new maintainer](https://github.com/gorilla/websocket/issues/370)**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
@ -20,6 +17,7 @@ Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
|
|||||||
* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command)
|
* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command)
|
||||||
* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo)
|
* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo)
|
||||||
* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch)
|
* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch)
|
||||||
|
* [Write buffer pool example](https://github.com/gorilla/websocket/tree/master/examples/bufferpool)
|
||||||
|
|
||||||
### Status
|
### Status
|
||||||
|
|
||||||
@ -36,4 +34,3 @@ package API is stable.
|
|||||||
The Gorilla WebSocket package passes the server tests in the [Autobahn Test
|
The Gorilla WebSocket package passes the server tests in the [Autobahn Test
|
||||||
Suite](https://github.com/crossbario/autobahn-testsuite) using the application in the [examples/autobahn
|
Suite](https://github.com/crossbario/autobahn-testsuite) using the application in the [examples/autobahn
|
||||||
subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn).
|
subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn).
|
||||||
|
|
||||||
|
44
vendor/github.com/gorilla/websocket/client.go
generated
vendored
44
vendor/github.com/gorilla/websocket/client.go
generated
vendored
@ -9,14 +9,18 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"log"
|
||||||
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptrace"
|
"net/http/httptrace"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrBadHandshake is returned when the server response to opening handshake is
|
// ErrBadHandshake is returned when the server response to opening handshake is
|
||||||
@ -224,6 +228,7 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
|||||||
k == "Connection" ||
|
k == "Connection" ||
|
||||||
k == "Sec-Websocket-Key" ||
|
k == "Sec-Websocket-Key" ||
|
||||||
k == "Sec-Websocket-Version" ||
|
k == "Sec-Websocket-Version" ||
|
||||||
|
//#nosec G101 (CWE-798): Potential HTTP request smuggling via parameter pollution
|
||||||
k == "Sec-Websocket-Extensions" ||
|
k == "Sec-Websocket-Extensions" ||
|
||||||
(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
|
(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
|
||||||
return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
|
return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
|
||||||
@ -289,7 +294,9 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
|||||||
}
|
}
|
||||||
err = c.SetDeadline(deadline)
|
err = c.SetDeadline(deadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Close()
|
if err := c.Close(); err != nil {
|
||||||
|
log.Printf("websocket: failed to close network connection: %v", err)
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
@ -303,7 +310,7 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if proxyURL != nil {
|
if proxyURL != nil {
|
||||||
dialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial))
|
dialer, err := proxy.FromURL(proxyURL, netDialerFunc(netDial))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@ -318,18 +325,20 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
|||||||
}
|
}
|
||||||
|
|
||||||
netConn, err := netDial("tcp", hostPort)
|
netConn, err := netDial("tcp", hostPort)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
if trace != nil && trace.GotConn != nil {
|
if trace != nil && trace.GotConn != nil {
|
||||||
trace.GotConn(httptrace.GotConnInfo{
|
trace.GotConn(httptrace.GotConnInfo{
|
||||||
Conn: netConn,
|
Conn: netConn,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if netConn != nil {
|
if netConn != nil {
|
||||||
netConn.Close()
|
if err := netConn.Close(); err != nil {
|
||||||
|
log.Printf("websocket: failed to close network connection: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -370,6 +379,17 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
|||||||
|
|
||||||
resp, err := http.ReadResponse(conn.br, req)
|
resp, err := http.ReadResponse(conn.br, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if d.TLSClientConfig != nil {
|
||||||
|
for _, proto := range d.TLSClientConfig.NextProtos {
|
||||||
|
if proto != "http/1.1" {
|
||||||
|
return nil, nil, fmt.Errorf(
|
||||||
|
"websocket: protocol %q was given but is not supported;"+
|
||||||
|
"sharing tls.Config with net/http Transport can cause this error: %w",
|
||||||
|
proto, err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,7 +408,7 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
|||||||
// debugging.
|
// debugging.
|
||||||
buf := make([]byte, 1024)
|
buf := make([]byte, 1024)
|
||||||
n, _ := io.ReadFull(resp.Body, buf)
|
n, _ := io.ReadFull(resp.Body, buf)
|
||||||
resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n]))
|
resp.Body = io.NopCloser(bytes.NewReader(buf[:n]))
|
||||||
return nil, resp, ErrBadHandshake
|
return nil, resp, ErrBadHandshake
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,17 +426,19 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
resp.Body = io.NopCloser(bytes.NewReader([]byte{}))
|
||||||
conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
|
conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
|
||||||
|
|
||||||
netConn.SetDeadline(time.Time{})
|
if err := netConn.SetDeadline(time.Time{}); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
netConn = nil // to avoid close in defer.
|
netConn = nil // to avoid close in defer.
|
||||||
return conn, resp, nil
|
return conn, resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return &tls.Config{}
|
return &tls.Config{MinVersion: tls.VersionTLS12}
|
||||||
}
|
}
|
||||||
return cfg.Clone()
|
return cfg.Clone()
|
||||||
}
|
}
|
||||||
|
9
vendor/github.com/gorilla/websocket/compression.go
generated
vendored
9
vendor/github.com/gorilla/websocket/compression.go
generated
vendored
@ -8,6 +8,7 @@ import (
|
|||||||
"compress/flate"
|
"compress/flate"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
@ -33,7 +34,9 @@ func decompressNoContextTakeover(r io.Reader) io.ReadCloser {
|
|||||||
"\x01\x00\x00\xff\xff"
|
"\x01\x00\x00\xff\xff"
|
||||||
|
|
||||||
fr, _ := flateReaderPool.Get().(io.ReadCloser)
|
fr, _ := flateReaderPool.Get().(io.ReadCloser)
|
||||||
fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil)
|
if err := fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
return &flateReadWrapper{fr}
|
return &flateReadWrapper{fr}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +135,9 @@ func (r *flateReadWrapper) Read(p []byte) (int, error) {
|
|||||||
// Preemptively place the reader back in the pool. This helps with
|
// Preemptively place the reader back in the pool. This helps with
|
||||||
// scenarios where the application does not call NextReader() soon after
|
// scenarios where the application does not call NextReader() soon after
|
||||||
// this final read.
|
// this final read.
|
||||||
r.Close()
|
if err := r.Close(); err != nil {
|
||||||
|
log.Printf("websocket: flateReadWrapper.Close() returned error: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
83
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
83
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
@ -6,11 +6,11 @@ package websocket
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"crypto/rand"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"log"
|
||||||
"math/rand"
|
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -181,13 +181,20 @@ var (
|
|||||||
errInvalidControlFrame = errors.New("websocket: invalid control frame")
|
errInvalidControlFrame = errors.New("websocket: invalid control frame")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// maskRand is an io.Reader for generating mask bytes. The reader is initialized
|
||||||
|
// to crypto/rand Reader. Tests swap the reader to a math/rand reader for
|
||||||
|
// reproducible results.
|
||||||
|
var maskRand = rand.Reader
|
||||||
|
|
||||||
|
// newMaskKey returns a new 32 bit value for masking client frames.
|
||||||
func newMaskKey() [4]byte {
|
func newMaskKey() [4]byte {
|
||||||
n := rand.Uint32()
|
var k [4]byte
|
||||||
return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
|
_, _ = io.ReadFull(maskRand, k[:])
|
||||||
|
return k
|
||||||
}
|
}
|
||||||
|
|
||||||
func hideTempErr(err error) error {
|
func hideTempErr(err error) error {
|
||||||
if e, ok := err.(net.Error); ok && e.Temporary() {
|
if e, ok := err.(net.Error); ok {
|
||||||
err = &netError{msg: e.Error(), timeout: e.Timeout()}
|
err = &netError{msg: e.Error(), timeout: e.Timeout()}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@ -372,7 +379,9 @@ func (c *Conn) read(n int) ([]byte, error) {
|
|||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
err = errUnexpectedEOF
|
err = errUnexpectedEOF
|
||||||
}
|
}
|
||||||
c.br.Discard(len(p))
|
if _, err := c.br.Discard(len(p)); err != nil {
|
||||||
|
return p, err
|
||||||
|
}
|
||||||
return p, err
|
return p, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,7 +396,9 @@ func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.conn.SetWriteDeadline(deadline)
|
if err := c.conn.SetWriteDeadline(deadline); err != nil {
|
||||||
|
return c.writeFatal(err)
|
||||||
|
}
|
||||||
if len(buf1) == 0 {
|
if len(buf1) == 0 {
|
||||||
_, err = c.conn.Write(buf0)
|
_, err = c.conn.Write(buf0)
|
||||||
} else {
|
} else {
|
||||||
@ -397,7 +408,7 @@ func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error
|
|||||||
return c.writeFatal(err)
|
return c.writeFatal(err)
|
||||||
}
|
}
|
||||||
if frameType == CloseMessage {
|
if frameType == CloseMessage {
|
||||||
c.writeFatal(ErrCloseSent)
|
_ = c.writeFatal(ErrCloseSent)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -438,7 +449,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er
|
|||||||
|
|
||||||
d := 1000 * time.Hour
|
d := 1000 * time.Hour
|
||||||
if !deadline.IsZero() {
|
if !deadline.IsZero() {
|
||||||
d = deadline.Sub(time.Now())
|
d = time.Until(deadline)
|
||||||
if d < 0 {
|
if d < 0 {
|
||||||
return errWriteTimeout
|
return errWriteTimeout
|
||||||
}
|
}
|
||||||
@ -460,13 +471,15 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.conn.SetWriteDeadline(deadline)
|
if err := c.conn.SetWriteDeadline(deadline); err != nil {
|
||||||
|
return c.writeFatal(err)
|
||||||
|
}
|
||||||
_, err = c.conn.Write(buf)
|
_, err = c.conn.Write(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.writeFatal(err)
|
return c.writeFatal(err)
|
||||||
}
|
}
|
||||||
if messageType == CloseMessage {
|
if messageType == CloseMessage {
|
||||||
c.writeFatal(ErrCloseSent)
|
_ = c.writeFatal(ErrCloseSent)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -477,7 +490,9 @@ func (c *Conn) beginMessage(mw *messageWriter, messageType int) error {
|
|||||||
// probably better to return an error in this situation, but we cannot
|
// probably better to return an error in this situation, but we cannot
|
||||||
// change this without breaking existing applications.
|
// change this without breaking existing applications.
|
||||||
if c.writer != nil {
|
if c.writer != nil {
|
||||||
c.writer.Close()
|
if err := c.writer.Close(); err != nil {
|
||||||
|
log.Printf("websocket: discarding writer close error: %v", err)
|
||||||
|
}
|
||||||
c.writer = nil
|
c.writer = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -630,7 +645,7 @@ func (w *messageWriter) flushFrame(final bool, extra []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if final {
|
if final {
|
||||||
w.endMessage(errWriteClosed)
|
_ = w.endMessage(errWriteClosed)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -795,7 +810,7 @@ func (c *Conn) advanceFrame() (int, error) {
|
|||||||
// 1. Skip remainder of previous frame.
|
// 1. Skip remainder of previous frame.
|
||||||
|
|
||||||
if c.readRemaining > 0 {
|
if c.readRemaining > 0 {
|
||||||
if _, err := io.CopyN(ioutil.Discard, c.br, c.readRemaining); err != nil {
|
if _, err := io.CopyN(io.Discard, c.br, c.readRemaining); err != nil {
|
||||||
return noFrame, err
|
return noFrame, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -817,7 +832,9 @@ func (c *Conn) advanceFrame() (int, error) {
|
|||||||
rsv2 := p[0]&rsv2Bit != 0
|
rsv2 := p[0]&rsv2Bit != 0
|
||||||
rsv3 := p[0]&rsv3Bit != 0
|
rsv3 := p[0]&rsv3Bit != 0
|
||||||
mask := p[1]&maskBit != 0
|
mask := p[1]&maskBit != 0
|
||||||
c.setReadRemaining(int64(p[1] & 0x7f))
|
if err := c.setReadRemaining(int64(p[1] & 0x7f)); err != nil {
|
||||||
|
return noFrame, err
|
||||||
|
}
|
||||||
|
|
||||||
c.readDecompress = false
|
c.readDecompress = false
|
||||||
if rsv1 {
|
if rsv1 {
|
||||||
@ -922,7 +939,9 @@ func (c *Conn) advanceFrame() (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.readLimit > 0 && c.readLength > c.readLimit {
|
if c.readLimit > 0 && c.readLength > c.readLimit {
|
||||||
c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait))
|
if err := c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait)); err != nil {
|
||||||
|
return noFrame, err
|
||||||
|
}
|
||||||
return noFrame, ErrReadLimit
|
return noFrame, ErrReadLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -934,7 +953,9 @@ func (c *Conn) advanceFrame() (int, error) {
|
|||||||
var payload []byte
|
var payload []byte
|
||||||
if c.readRemaining > 0 {
|
if c.readRemaining > 0 {
|
||||||
payload, err = c.read(int(c.readRemaining))
|
payload, err = c.read(int(c.readRemaining))
|
||||||
c.setReadRemaining(0)
|
if err := c.setReadRemaining(0); err != nil {
|
||||||
|
return noFrame, err
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return noFrame, err
|
return noFrame, err
|
||||||
}
|
}
|
||||||
@ -981,7 +1002,9 @@ func (c *Conn) handleProtocolError(message string) error {
|
|||||||
if len(data) > maxControlFramePayloadSize {
|
if len(data) > maxControlFramePayloadSize {
|
||||||
data = data[:maxControlFramePayloadSize]
|
data = data[:maxControlFramePayloadSize]
|
||||||
}
|
}
|
||||||
c.WriteControl(CloseMessage, data, time.Now().Add(writeWait))
|
if err := c.WriteControl(CloseMessage, data, time.Now().Add(writeWait)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return errors.New("websocket: " + message)
|
return errors.New("websocket: " + message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -998,7 +1021,9 @@ func (c *Conn) handleProtocolError(message string) error {
|
|||||||
func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
||||||
// Close previous reader, only relevant for decompression.
|
// Close previous reader, only relevant for decompression.
|
||||||
if c.reader != nil {
|
if c.reader != nil {
|
||||||
c.reader.Close()
|
if err := c.reader.Close(); err != nil {
|
||||||
|
log.Printf("websocket: discarding reader close error: %v", err)
|
||||||
|
}
|
||||||
c.reader = nil
|
c.reader = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1054,7 +1079,9 @@ func (r *messageReader) Read(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
rem := c.readRemaining
|
rem := c.readRemaining
|
||||||
rem -= int64(n)
|
rem -= int64(n)
|
||||||
c.setReadRemaining(rem)
|
if err := c.setReadRemaining(rem); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
if c.readRemaining > 0 && c.readErr == io.EOF {
|
if c.readRemaining > 0 && c.readErr == io.EOF {
|
||||||
c.readErr = errUnexpectedEOF
|
c.readErr = errUnexpectedEOF
|
||||||
}
|
}
|
||||||
@ -1094,7 +1121,7 @@ func (c *Conn) ReadMessage() (messageType int, p []byte, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return messageType, nil, err
|
return messageType, nil, err
|
||||||
}
|
}
|
||||||
p, err = ioutil.ReadAll(r)
|
p, err = io.ReadAll(r)
|
||||||
return messageType, p, err
|
return messageType, p, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1136,7 +1163,9 @@ func (c *Conn) SetCloseHandler(h func(code int, text string) error) {
|
|||||||
if h == nil {
|
if h == nil {
|
||||||
h = func(code int, text string) error {
|
h = func(code int, text string) error {
|
||||||
message := FormatCloseMessage(code, "")
|
message := FormatCloseMessage(code, "")
|
||||||
c.WriteControl(CloseMessage, message, time.Now().Add(writeWait))
|
if err := c.WriteControl(CloseMessage, message, time.Now().Add(writeWait)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1161,7 +1190,7 @@ func (c *Conn) SetPingHandler(h func(appData string) error) {
|
|||||||
err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait))
|
err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait))
|
||||||
if err == ErrCloseSent {
|
if err == ErrCloseSent {
|
||||||
return nil
|
return nil
|
||||||
} else if e, ok := err.(net.Error); ok && e.Temporary() {
|
} else if _, ok := err.(net.Error); ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@ -1189,8 +1218,16 @@ func (c *Conn) SetPongHandler(h func(appData string) error) {
|
|||||||
c.handlePong = h
|
c.handlePong = h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NetConn returns the underlying connection that is wrapped by c.
|
||||||
|
// Note that writing to or reading from this connection directly will corrupt the
|
||||||
|
// WebSocket connection.
|
||||||
|
func (c *Conn) NetConn() net.Conn {
|
||||||
|
return c.conn
|
||||||
|
}
|
||||||
|
|
||||||
// UnderlyingConn returns the internal net.Conn. This can be used to further
|
// UnderlyingConn returns the internal net.Conn. This can be used to further
|
||||||
// modifications to connection specific flags.
|
// modifications to connection specific flags.
|
||||||
|
// Deprecated: Use the NetConn method.
|
||||||
func (c *Conn) UnderlyingConn() net.Conn {
|
func (c *Conn) UnderlyingConn() net.Conn {
|
||||||
return c.conn
|
return c.conn
|
||||||
}
|
}
|
||||||
|
4
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
4
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
@ -9,6 +9,7 @@ package websocket
|
|||||||
|
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
|
// #nosec G103 -- (CWE-242) Has been audited
|
||||||
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
||||||
|
|
||||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||||
@ -22,6 +23,7 @@ func maskBytes(key [4]byte, pos int, b []byte) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mask one byte at a time to word boundary.
|
// Mask one byte at a time to word boundary.
|
||||||
|
//#nosec G103 -- (CWE-242) Has been audited
|
||||||
if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 {
|
if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 {
|
||||||
n = wordSize - n
|
n = wordSize - n
|
||||||
for i := range b[:n] {
|
for i := range b[:n] {
|
||||||
@ -36,11 +38,13 @@ func maskBytes(key [4]byte, pos int, b []byte) int {
|
|||||||
for i := range k {
|
for i := range k {
|
||||||
k[i] = key[(pos+i)&3]
|
k[i] = key[(pos+i)&3]
|
||||||
}
|
}
|
||||||
|
//#nosec G103 -- (CWE-242) Has been audited
|
||||||
kw := *(*uintptr)(unsafe.Pointer(&k))
|
kw := *(*uintptr)(unsafe.Pointer(&k))
|
||||||
|
|
||||||
// Mask one word at a time.
|
// Mask one word at a time.
|
||||||
n := (len(b) / wordSize) * wordSize
|
n := (len(b) / wordSize) * wordSize
|
||||||
for i := 0; i < n; i += wordSize {
|
for i := 0; i < n; i += wordSize {
|
||||||
|
//#nosec G103 -- (CWE-242) Has been audited
|
||||||
*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw
|
*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
17
vendor/github.com/gorilla/websocket/proxy.go
generated
vendored
17
vendor/github.com/gorilla/websocket/proxy.go
generated
vendored
@ -8,10 +8,13 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
type netDialerFunc func(network, addr string) (net.Conn, error)
|
type netDialerFunc func(network, addr string) (net.Conn, error)
|
||||||
@ -21,7 +24,7 @@ func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) {
|
proxy.RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy.Dialer) (proxy.Dialer, error) {
|
||||||
return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil
|
return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -55,7 +58,9 @@ func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := connectReq.Write(conn); err != nil {
|
if err := connectReq.Write(conn); err != nil {
|
||||||
conn.Close()
|
if err := conn.Close(); err != nil {
|
||||||
|
log.Printf("httpProxyDialer: failed to close connection: %v", err)
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,12 +69,16 @@ func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error)
|
|||||||
br := bufio.NewReader(conn)
|
br := bufio.NewReader(conn)
|
||||||
resp, err := http.ReadResponse(br, connectReq)
|
resp, err := http.ReadResponse(br, connectReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.Close()
|
if err := conn.Close(); err != nil {
|
||||||
|
log.Printf("httpProxyDialer: failed to close connection: %v", err)
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
conn.Close()
|
if err := conn.Close(); err != nil {
|
||||||
|
log.Printf("httpProxyDialer: failed to close connection: %v", err)
|
||||||
|
}
|
||||||
f := strings.SplitN(resp.Status, " ", 2)
|
f := strings.SplitN(resp.Status, " ", 2)
|
||||||
return nil, errors.New(f[1])
|
return nil, errors.New(f[1])
|
||||||
}
|
}
|
||||||
|
42
vendor/github.com/gorilla/websocket/server.go
generated
vendored
42
vendor/github.com/gorilla/websocket/server.go
generated
vendored
@ -8,6 +8,7 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
@ -154,8 +155,8 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
|||||||
}
|
}
|
||||||
|
|
||||||
challengeKey := r.Header.Get("Sec-Websocket-Key")
|
challengeKey := r.Header.Get("Sec-Websocket-Key")
|
||||||
if challengeKey == "" {
|
if !isValidChallengeKey(challengeKey) {
|
||||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header is missing or blank")
|
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header must be Base64 encoded value of 16-byte in length")
|
||||||
}
|
}
|
||||||
|
|
||||||
subprotocol := u.selectSubprotocol(r, responseHeader)
|
subprotocol := u.selectSubprotocol(r, responseHeader)
|
||||||
@ -183,7 +184,9 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
|||||||
}
|
}
|
||||||
|
|
||||||
if brw.Reader.Buffered() > 0 {
|
if brw.Reader.Buffered() > 0 {
|
||||||
netConn.Close()
|
if err := netConn.Close(); err != nil {
|
||||||
|
log.Printf("websocket: failed to close network connection: %v", err)
|
||||||
|
}
|
||||||
return nil, errors.New("websocket: client sent data before handshake is complete")
|
return nil, errors.New("websocket: client sent data before handshake is complete")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,17 +251,34 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
|||||||
p = append(p, "\r\n"...)
|
p = append(p, "\r\n"...)
|
||||||
|
|
||||||
// Clear deadlines set by HTTP server.
|
// Clear deadlines set by HTTP server.
|
||||||
netConn.SetDeadline(time.Time{})
|
if err := netConn.SetDeadline(time.Time{}); err != nil {
|
||||||
|
if err := netConn.Close(); err != nil {
|
||||||
|
log.Printf("websocket: failed to close network connection: %v", err)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if u.HandshakeTimeout > 0 {
|
if u.HandshakeTimeout > 0 {
|
||||||
netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout))
|
if err := netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout)); err != nil {
|
||||||
|
if err := netConn.Close(); err != nil {
|
||||||
|
log.Printf("websocket: failed to close network connection: %v", err)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if _, err = netConn.Write(p); err != nil {
|
if _, err = netConn.Write(p); err != nil {
|
||||||
netConn.Close()
|
if err := netConn.Close(); err != nil {
|
||||||
|
log.Printf("websocket: failed to close network connection: %v", err)
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if u.HandshakeTimeout > 0 {
|
if u.HandshakeTimeout > 0 {
|
||||||
netConn.SetWriteDeadline(time.Time{})
|
if err := netConn.SetWriteDeadline(time.Time{}); err != nil {
|
||||||
|
if err := netConn.Close(); err != nil {
|
||||||
|
log.Printf("websocket: failed to close network connection: %v", err)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
@ -356,8 +376,12 @@ func bufioWriterBuffer(originalWriter io.Writer, bw *bufio.Writer) []byte {
|
|||||||
// bufio.Writer's underlying writer.
|
// bufio.Writer's underlying writer.
|
||||||
var wh writeHook
|
var wh writeHook
|
||||||
bw.Reset(&wh)
|
bw.Reset(&wh)
|
||||||
bw.WriteByte(0)
|
if err := bw.WriteByte(0); err != nil {
|
||||||
bw.Flush()
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := bw.Flush(); err != nil {
|
||||||
|
log.Printf("websocket: bufioWriterBuffer: Flush: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
bw.Reset(originalWriter)
|
bw.Reset(originalWriter)
|
||||||
|
|
||||||
|
3
vendor/github.com/gorilla/websocket/tls_handshake.go
generated
vendored
3
vendor/github.com/gorilla/websocket/tls_handshake.go
generated
vendored
@ -1,6 +1,3 @@
|
|||||||
//go:build go1.17
|
|
||||||
// +build go1.17
|
|
||||||
|
|
||||||
package websocket
|
package websocket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
21
vendor/github.com/gorilla/websocket/tls_handshake_116.go
generated
vendored
21
vendor/github.com/gorilla/websocket/tls_handshake_116.go
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
//go:build !go1.17
|
|
||||||
// +build !go1.17
|
|
||||||
|
|
||||||
package websocket
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
)
|
|
||||||
|
|
||||||
func doHandshake(ctx context.Context, tlsConn *tls.Conn, cfg *tls.Config) error {
|
|
||||||
if err := tlsConn.Handshake(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !cfg.InsecureSkipVerify {
|
|
||||||
if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
19
vendor/github.com/gorilla/websocket/util.go
generated
vendored
19
vendor/github.com/gorilla/websocket/util.go
generated
vendored
@ -6,7 +6,7 @@ package websocket
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha1"
|
"crypto/sha1" //#nosec G505 -- (CWE-327) https://datatracker.ietf.org/doc/html/rfc6455#page-54
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -17,7 +17,7 @@ import (
|
|||||||
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
||||||
|
|
||||||
func computeAcceptKey(challengeKey string) string {
|
func computeAcceptKey(challengeKey string) string {
|
||||||
h := sha1.New()
|
h := sha1.New() //#nosec G401 -- (CWE-326) https://datatracker.ietf.org/doc/html/rfc6455#page-54
|
||||||
h.Write([]byte(challengeKey))
|
h.Write([]byte(challengeKey))
|
||||||
h.Write(keyGUID)
|
h.Write(keyGUID)
|
||||||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||||
@ -281,3 +281,18 @@ headers:
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isValidChallengeKey checks if the argument meets RFC6455 specification.
|
||||||
|
func isValidChallengeKey(s string) bool {
|
||||||
|
// From RFC6455:
|
||||||
|
//
|
||||||
|
// A |Sec-WebSocket-Key| header field with a base64-encoded (see
|
||||||
|
// Section 4 of [RFC4648]) value that, when decoded, is 16 bytes in
|
||||||
|
// length.
|
||||||
|
|
||||||
|
if s == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
decoded, err := base64.StdEncoding.DecodeString(s)
|
||||||
|
return err == nil && len(decoded) == 16
|
||||||
|
}
|
||||||
|
473
vendor/github.com/gorilla/websocket/x_net_proxy.go
generated
vendored
473
vendor/github.com/gorilla/websocket/x_net_proxy.go
generated
vendored
@ -1,473 +0,0 @@
|
|||||||
// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
|
|
||||||
//go:generate bundle -o x_net_proxy.go golang.org/x/net/proxy
|
|
||||||
|
|
||||||
// Package proxy provides support for a variety of protocols to proxy network
|
|
||||||
// data.
|
|
||||||
//
|
|
||||||
|
|
||||||
package websocket
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type proxy_direct struct{}
|
|
||||||
|
|
||||||
// Direct is a direct proxy: one that makes network connections directly.
|
|
||||||
var proxy_Direct = proxy_direct{}
|
|
||||||
|
|
||||||
func (proxy_direct) Dial(network, addr string) (net.Conn, error) {
|
|
||||||
return net.Dial(network, addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A PerHost directs connections to a default Dialer unless the host name
|
|
||||||
// requested matches one of a number of exceptions.
|
|
||||||
type proxy_PerHost struct {
|
|
||||||
def, bypass proxy_Dialer
|
|
||||||
|
|
||||||
bypassNetworks []*net.IPNet
|
|
||||||
bypassIPs []net.IP
|
|
||||||
bypassZones []string
|
|
||||||
bypassHosts []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPerHost returns a PerHost Dialer that directs connections to either
|
|
||||||
// defaultDialer or bypass, depending on whether the connection matches one of
|
|
||||||
// the configured rules.
|
|
||||||
func proxy_NewPerHost(defaultDialer, bypass proxy_Dialer) *proxy_PerHost {
|
|
||||||
return &proxy_PerHost{
|
|
||||||
def: defaultDialer,
|
|
||||||
bypass: bypass,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial connects to the address addr on the given network through either
|
|
||||||
// defaultDialer or bypass.
|
|
||||||
func (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) {
|
|
||||||
host, _, err := net.SplitHostPort(addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return p.dialerForRequest(host).Dial(network, addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer {
|
|
||||||
if ip := net.ParseIP(host); ip != nil {
|
|
||||||
for _, net := range p.bypassNetworks {
|
|
||||||
if net.Contains(ip) {
|
|
||||||
return p.bypass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, bypassIP := range p.bypassIPs {
|
|
||||||
if bypassIP.Equal(ip) {
|
|
||||||
return p.bypass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p.def
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, zone := range p.bypassZones {
|
|
||||||
if strings.HasSuffix(host, zone) {
|
|
||||||
return p.bypass
|
|
||||||
}
|
|
||||||
if host == zone[1:] {
|
|
||||||
// For a zone ".example.com", we match "example.com"
|
|
||||||
// too.
|
|
||||||
return p.bypass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, bypassHost := range p.bypassHosts {
|
|
||||||
if bypassHost == host {
|
|
||||||
return p.bypass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p.def
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddFromString parses a string that contains comma-separated values
|
|
||||||
// specifying hosts that should use the bypass proxy. Each value is either an
|
|
||||||
// IP address, a CIDR range, a zone (*.example.com) or a host name
|
|
||||||
// (localhost). A best effort is made to parse the string and errors are
|
|
||||||
// ignored.
|
|
||||||
func (p *proxy_PerHost) AddFromString(s string) {
|
|
||||||
hosts := strings.Split(s, ",")
|
|
||||||
for _, host := range hosts {
|
|
||||||
host = strings.TrimSpace(host)
|
|
||||||
if len(host) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.Contains(host, "/") {
|
|
||||||
// We assume that it's a CIDR address like 127.0.0.0/8
|
|
||||||
if _, net, err := net.ParseCIDR(host); err == nil {
|
|
||||||
p.AddNetwork(net)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ip := net.ParseIP(host); ip != nil {
|
|
||||||
p.AddIP(ip)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(host, "*.") {
|
|
||||||
p.AddZone(host[1:])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
p.AddHost(host)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddIP specifies an IP address that will use the bypass proxy. Note that
|
|
||||||
// this will only take effect if a literal IP address is dialed. A connection
|
|
||||||
// to a named host will never match an IP.
|
|
||||||
func (p *proxy_PerHost) AddIP(ip net.IP) {
|
|
||||||
p.bypassIPs = append(p.bypassIPs, ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddNetwork specifies an IP range that will use the bypass proxy. Note that
|
|
||||||
// this will only take effect if a literal IP address is dialed. A connection
|
|
||||||
// to a named host will never match.
|
|
||||||
func (p *proxy_PerHost) AddNetwork(net *net.IPNet) {
|
|
||||||
p.bypassNetworks = append(p.bypassNetworks, net)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
|
|
||||||
// "example.com" matches "example.com" and all of its subdomains.
|
|
||||||
func (p *proxy_PerHost) AddZone(zone string) {
|
|
||||||
if strings.HasSuffix(zone, ".") {
|
|
||||||
zone = zone[:len(zone)-1]
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(zone, ".") {
|
|
||||||
zone = "." + zone
|
|
||||||
}
|
|
||||||
p.bypassZones = append(p.bypassZones, zone)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddHost specifies a host name that will use the bypass proxy.
|
|
||||||
func (p *proxy_PerHost) AddHost(host string) {
|
|
||||||
if strings.HasSuffix(host, ".") {
|
|
||||||
host = host[:len(host)-1]
|
|
||||||
}
|
|
||||||
p.bypassHosts = append(p.bypassHosts, host)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Dialer is a means to establish a connection.
|
|
||||||
type proxy_Dialer interface {
|
|
||||||
// Dial connects to the given address via the proxy.
|
|
||||||
Dial(network, addr string) (c net.Conn, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auth contains authentication parameters that specific Dialers may require.
|
|
||||||
type proxy_Auth struct {
|
|
||||||
User, Password string
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromEnvironment returns the dialer specified by the proxy related variables in
|
|
||||||
// the environment.
|
|
||||||
func proxy_FromEnvironment() proxy_Dialer {
|
|
||||||
allProxy := proxy_allProxyEnv.Get()
|
|
||||||
if len(allProxy) == 0 {
|
|
||||||
return proxy_Direct
|
|
||||||
}
|
|
||||||
|
|
||||||
proxyURL, err := url.Parse(allProxy)
|
|
||||||
if err != nil {
|
|
||||||
return proxy_Direct
|
|
||||||
}
|
|
||||||
proxy, err := proxy_FromURL(proxyURL, proxy_Direct)
|
|
||||||
if err != nil {
|
|
||||||
return proxy_Direct
|
|
||||||
}
|
|
||||||
|
|
||||||
noProxy := proxy_noProxyEnv.Get()
|
|
||||||
if len(noProxy) == 0 {
|
|
||||||
return proxy
|
|
||||||
}
|
|
||||||
|
|
||||||
perHost := proxy_NewPerHost(proxy, proxy_Direct)
|
|
||||||
perHost.AddFromString(noProxy)
|
|
||||||
return perHost
|
|
||||||
}
|
|
||||||
|
|
||||||
// proxySchemes is a map from URL schemes to a function that creates a Dialer
|
|
||||||
// from a URL with such a scheme.
|
|
||||||
var proxy_proxySchemes map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error)
|
|
||||||
|
|
||||||
// RegisterDialerType takes a URL scheme and a function to generate Dialers from
|
|
||||||
// a URL with that scheme and a forwarding Dialer. Registered schemes are used
|
|
||||||
// by FromURL.
|
|
||||||
func proxy_RegisterDialerType(scheme string, f func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) {
|
|
||||||
if proxy_proxySchemes == nil {
|
|
||||||
proxy_proxySchemes = make(map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error))
|
|
||||||
}
|
|
||||||
proxy_proxySchemes[scheme] = f
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromURL returns a Dialer given a URL specification and an underlying
|
|
||||||
// Dialer for it to make network requests.
|
|
||||||
func proxy_FromURL(u *url.URL, forward proxy_Dialer) (proxy_Dialer, error) {
|
|
||||||
var auth *proxy_Auth
|
|
||||||
if u.User != nil {
|
|
||||||
auth = new(proxy_Auth)
|
|
||||||
auth.User = u.User.Username()
|
|
||||||
if p, ok := u.User.Password(); ok {
|
|
||||||
auth.Password = p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch u.Scheme {
|
|
||||||
case "socks5":
|
|
||||||
return proxy_SOCKS5("tcp", u.Host, auth, forward)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the scheme doesn't match any of the built-in schemes, see if it
|
|
||||||
// was registered by another package.
|
|
||||||
if proxy_proxySchemes != nil {
|
|
||||||
if f, ok := proxy_proxySchemes[u.Scheme]; ok {
|
|
||||||
return f(u, forward)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
proxy_allProxyEnv = &proxy_envOnce{
|
|
||||||
names: []string{"ALL_PROXY", "all_proxy"},
|
|
||||||
}
|
|
||||||
proxy_noProxyEnv = &proxy_envOnce{
|
|
||||||
names: []string{"NO_PROXY", "no_proxy"},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// envOnce looks up an environment variable (optionally by multiple
|
|
||||||
// names) once. It mitigates expensive lookups on some platforms
|
|
||||||
// (e.g. Windows).
|
|
||||||
// (Borrowed from net/http/transport.go)
|
|
||||||
type proxy_envOnce struct {
|
|
||||||
names []string
|
|
||||||
once sync.Once
|
|
||||||
val string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *proxy_envOnce) Get() string {
|
|
||||||
e.once.Do(e.init)
|
|
||||||
return e.val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *proxy_envOnce) init() {
|
|
||||||
for _, n := range e.names {
|
|
||||||
e.val = os.Getenv(n)
|
|
||||||
if e.val != "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
|
|
||||||
// with an optional username and password. See RFC 1928 and RFC 1929.
|
|
||||||
func proxy_SOCKS5(network, addr string, auth *proxy_Auth, forward proxy_Dialer) (proxy_Dialer, error) {
|
|
||||||
s := &proxy_socks5{
|
|
||||||
network: network,
|
|
||||||
addr: addr,
|
|
||||||
forward: forward,
|
|
||||||
}
|
|
||||||
if auth != nil {
|
|
||||||
s.user = auth.User
|
|
||||||
s.password = auth.Password
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type proxy_socks5 struct {
|
|
||||||
user, password string
|
|
||||||
network, addr string
|
|
||||||
forward proxy_Dialer
|
|
||||||
}
|
|
||||||
|
|
||||||
const proxy_socks5Version = 5
|
|
||||||
|
|
||||||
const (
|
|
||||||
proxy_socks5AuthNone = 0
|
|
||||||
proxy_socks5AuthPassword = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
const proxy_socks5Connect = 1
|
|
||||||
|
|
||||||
const (
|
|
||||||
proxy_socks5IP4 = 1
|
|
||||||
proxy_socks5Domain = 3
|
|
||||||
proxy_socks5IP6 = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
var proxy_socks5Errors = []string{
|
|
||||||
"",
|
|
||||||
"general failure",
|
|
||||||
"connection forbidden",
|
|
||||||
"network unreachable",
|
|
||||||
"host unreachable",
|
|
||||||
"connection refused",
|
|
||||||
"TTL expired",
|
|
||||||
"command not supported",
|
|
||||||
"address type not supported",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial connects to the address addr on the given network via the SOCKS5 proxy.
|
|
||||||
func (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) {
|
|
||||||
switch network {
|
|
||||||
case "tcp", "tcp6", "tcp4":
|
|
||||||
default:
|
|
||||||
return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network)
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := s.forward.Dial(s.network, s.addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := s.connect(conn, addr); err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// connect takes an existing connection to a socks5 proxy server,
|
|
||||||
// and commands the server to extend that connection to target,
|
|
||||||
// which must be a canonical address with a host and port.
|
|
||||||
func (s *proxy_socks5) connect(conn net.Conn, target string) error {
|
|
||||||
host, portStr, err := net.SplitHostPort(target)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
port, err := strconv.Atoi(portStr)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("proxy: failed to parse port number: " + portStr)
|
|
||||||
}
|
|
||||||
if port < 1 || port > 0xffff {
|
|
||||||
return errors.New("proxy: port number out of range: " + portStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// the size here is just an estimate
|
|
||||||
buf := make([]byte, 0, 6+len(host))
|
|
||||||
|
|
||||||
buf = append(buf, proxy_socks5Version)
|
|
||||||
if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
|
|
||||||
buf = append(buf, 2 /* num auth methods */, proxy_socks5AuthNone, proxy_socks5AuthPassword)
|
|
||||||
} else {
|
|
||||||
buf = append(buf, 1 /* num auth methods */, proxy_socks5AuthNone)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := conn.Write(buf); err != nil {
|
|
||||||
return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
|
||||||
return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
if buf[0] != 5 {
|
|
||||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
|
|
||||||
}
|
|
||||||
if buf[1] == 0xff {
|
|
||||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
|
|
||||||
}
|
|
||||||
|
|
||||||
// See RFC 1929
|
|
||||||
if buf[1] == proxy_socks5AuthPassword {
|
|
||||||
buf = buf[:0]
|
|
||||||
buf = append(buf, 1 /* password protocol version */)
|
|
||||||
buf = append(buf, uint8(len(s.user)))
|
|
||||||
buf = append(buf, s.user...)
|
|
||||||
buf = append(buf, uint8(len(s.password)))
|
|
||||||
buf = append(buf, s.password...)
|
|
||||||
|
|
||||||
if _, err := conn.Write(buf); err != nil {
|
|
||||||
return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
|
||||||
return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if buf[1] != 0 {
|
|
||||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = buf[:0]
|
|
||||||
buf = append(buf, proxy_socks5Version, proxy_socks5Connect, 0 /* reserved */)
|
|
||||||
|
|
||||||
if ip := net.ParseIP(host); ip != nil {
|
|
||||||
if ip4 := ip.To4(); ip4 != nil {
|
|
||||||
buf = append(buf, proxy_socks5IP4)
|
|
||||||
ip = ip4
|
|
||||||
} else {
|
|
||||||
buf = append(buf, proxy_socks5IP6)
|
|
||||||
}
|
|
||||||
buf = append(buf, ip...)
|
|
||||||
} else {
|
|
||||||
if len(host) > 255 {
|
|
||||||
return errors.New("proxy: destination host name too long: " + host)
|
|
||||||
}
|
|
||||||
buf = append(buf, proxy_socks5Domain)
|
|
||||||
buf = append(buf, byte(len(host)))
|
|
||||||
buf = append(buf, host...)
|
|
||||||
}
|
|
||||||
buf = append(buf, byte(port>>8), byte(port))
|
|
||||||
|
|
||||||
if _, err := conn.Write(buf); err != nil {
|
|
||||||
return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := io.ReadFull(conn, buf[:4]); err != nil {
|
|
||||||
return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
failure := "unknown error"
|
|
||||||
if int(buf[1]) < len(proxy_socks5Errors) {
|
|
||||||
failure = proxy_socks5Errors[buf[1]]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(failure) > 0 {
|
|
||||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
|
|
||||||
}
|
|
||||||
|
|
||||||
bytesToDiscard := 0
|
|
||||||
switch buf[3] {
|
|
||||||
case proxy_socks5IP4:
|
|
||||||
bytesToDiscard = net.IPv4len
|
|
||||||
case proxy_socks5IP6:
|
|
||||||
bytesToDiscard = net.IPv6len
|
|
||||||
case proxy_socks5Domain:
|
|
||||||
_, err := io.ReadFull(conn, buf[:1])
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
bytesToDiscard = int(buf[0])
|
|
||||||
default:
|
|
||||||
return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cap(buf) < bytesToDiscard {
|
|
||||||
buf = make([]byte, bytesToDiscard)
|
|
||||||
} else {
|
|
||||||
buf = buf[:bytesToDiscard]
|
|
||||||
}
|
|
||||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
||||||
return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also need to discard the port number
|
|
||||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
|
||||||
return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
5
vendor/github.com/graph-gophers/graphql-go/.gitignore
generated
vendored
5
vendor/github.com/graph-gophers/graphql-go/.gitignore
generated
vendored
@ -1,5 +0,0 @@
|
|||||||
/.idea
|
|
||||||
/.vscode
|
|
||||||
/internal/validation/testdata/graphql-js
|
|
||||||
/internal/validation/testdata/node_modules
|
|
||||||
/vendor
|
|
35
vendor/github.com/graph-gophers/graphql-go/.golangci.yml
generated
vendored
35
vendor/github.com/graph-gophers/graphql-go/.golangci.yml
generated
vendored
@ -1,35 +0,0 @@
|
|||||||
run:
|
|
||||||
timeout: 5m
|
|
||||||
|
|
||||||
linters-settings:
|
|
||||||
gofmt:
|
|
||||||
simplify: true
|
|
||||||
govet:
|
|
||||||
check-shadowing: true
|
|
||||||
enable-all: true
|
|
||||||
disable:
|
|
||||||
- fieldalignment
|
|
||||||
- deepequalerrors # remove later
|
|
||||||
|
|
||||||
linters:
|
|
||||||
disable-all: true
|
|
||||||
enable:
|
|
||||||
- deadcode
|
|
||||||
- gofmt
|
|
||||||
- gosimple
|
|
||||||
- govet
|
|
||||||
- ineffassign
|
|
||||||
- exportloopref
|
|
||||||
- structcheck
|
|
||||||
- staticcheck
|
|
||||||
- unconvert
|
|
||||||
- unused
|
|
||||||
- varcheck
|
|
||||||
- misspell
|
|
||||||
- goimports
|
|
||||||
|
|
||||||
issues:
|
|
||||||
exclude-rules:
|
|
||||||
- linters:
|
|
||||||
- unused
|
|
||||||
path: "graphql_test.go"
|
|
10
vendor/github.com/graph-gophers/graphql-go/CHANGELOG.md
generated
vendored
10
vendor/github.com/graph-gophers/graphql-go/CHANGELOG.md
generated
vendored
@ -1,10 +0,0 @@
|
|||||||
CHANGELOG
|
|
||||||
|
|
||||||
[v1.1.0](https://github.com/graph-gophers/graphql-go/releases/tag/v1.1.0) Release v1.1.0
|
|
||||||
* [FEATURE] Add types package #437
|
|
||||||
* [FEATURE] Expose `packer.Unmarshaler` as `decode.Unmarshaler` to the public #450
|
|
||||||
* [FEATURE] Add location fields to type definitions #454
|
|
||||||
* [FEATURE] `errors.Errorf` preserves original error similar to `fmt.Errorf` #456
|
|
||||||
* [BUGFIX] Fix duplicated __typename in response (fixes #369) #443
|
|
||||||
|
|
||||||
[v1.0.0](https://github.com/graph-gophers/graphql-go/releases/tag/v1.0.0) Initial release
|
|
13
vendor/github.com/graph-gophers/graphql-go/CONTRIBUTING.md
generated
vendored
13
vendor/github.com/graph-gophers/graphql-go/CONTRIBUTING.md
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
## Contributing
|
|
||||||
|
|
||||||
- With issues:
|
|
||||||
- Use the search tool before opening a new issue.
|
|
||||||
- Please provide source code and commit sha if you found a bug.
|
|
||||||
- Review existing issues and provide feedback or react to them.
|
|
||||||
|
|
||||||
- With pull requests:
|
|
||||||
- Open your pull request against `master`
|
|
||||||
- Your pull request should have no more than two commits, if not you should squash them.
|
|
||||||
- It should pass all tests in the available continuous integrations systems such as TravisCI.
|
|
||||||
- You should add/modify tests to cover your proposed code changes.
|
|
||||||
- If your pull request contains a new feature, please document it on the README.
|
|
24
vendor/github.com/graph-gophers/graphql-go/LICENSE
generated
vendored
24
vendor/github.com/graph-gophers/graphql-go/LICENSE
generated
vendored
@ -1,24 +0,0 @@
|
|||||||
Copyright (c) 2016 Richard Musiol. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
169
vendor/github.com/graph-gophers/graphql-go/README.md
generated
vendored
169
vendor/github.com/graph-gophers/graphql-go/README.md
generated
vendored
@ -1,169 +0,0 @@
|
|||||||
# graphql-go [![Sourcegraph](https://sourcegraph.com/github.com/graph-gophers/graphql-go/-/badge.svg)](https://sourcegraph.com/github.com/graph-gophers/graphql-go?badge) [![Build Status](https://graph-gophers.semaphoreci.com/badges/graphql-go/branches/master.svg?style=shields)](https://graph-gophers.semaphoreci.com/projects/graphql-go) [![GoDoc](https://godoc.org/github.com/graph-gophers/graphql-go?status.svg)](https://godoc.org/github.com/graph-gophers/graphql-go)
|
|
||||||
|
|
||||||
<p align="center"><img src="docs/img/logo.png" width="300"></p>
|
|
||||||
|
|
||||||
The goal of this project is to provide full support of the [GraphQL draft specification](https://facebook.github.io/graphql/draft) with a set of idiomatic, easy to use Go packages.
|
|
||||||
|
|
||||||
While still under heavy development (`internal` APIs are almost certainly subject to change), this library is
|
|
||||||
safe for production use.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- minimal API
|
|
||||||
- support for `context.Context`
|
|
||||||
- support for the `OpenTracing` standard
|
|
||||||
- schema type-checking against resolvers
|
|
||||||
- resolvers are matched to the schema based on method sets (can resolve a GraphQL schema with a Go interface or Go struct).
|
|
||||||
- handles panics in resolvers
|
|
||||||
- parallel execution of resolvers
|
|
||||||
- subscriptions
|
|
||||||
- [sample WS transport](https://github.com/graph-gophers/graphql-transport-ws)
|
|
||||||
|
|
||||||
## Roadmap
|
|
||||||
|
|
||||||
We're trying out the GitHub Project feature to manage `graphql-go`'s [development roadmap](https://github.com/graph-gophers/graphql-go/projects/1).
|
|
||||||
Feedback is welcome and appreciated.
|
|
||||||
|
|
||||||
## (Some) Documentation
|
|
||||||
|
|
||||||
### Basic Sample
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
graphql "github.com/graph-gophers/graphql-go"
|
|
||||||
"github.com/graph-gophers/graphql-go/relay"
|
|
||||||
)
|
|
||||||
|
|
||||||
type query struct{}
|
|
||||||
|
|
||||||
func (_ *query) Hello() string { return "Hello, world!" }
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
s := `
|
|
||||||
type Query {
|
|
||||||
hello: String!
|
|
||||||
}
|
|
||||||
`
|
|
||||||
schema := graphql.MustParseSchema(s, &query{})
|
|
||||||
http.Handle("/query", &relay.Handler{Schema: schema})
|
|
||||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
To test:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
curl -XPOST -d '{"query": "{ hello }"}' localhost:8080/query
|
|
||||||
```
|
|
||||||
|
|
||||||
### Resolvers
|
|
||||||
|
|
||||||
A resolver must have one method or field for each field of the GraphQL type it resolves. The method or field name has to be [exported](https://golang.org/ref/spec#Exported_identifiers) and match the schema's field's name in a non-case-sensitive way.
|
|
||||||
You can use struct fields as resolvers by using `SchemaOpt: UseFieldResolvers()`. For example,
|
|
||||||
```
|
|
||||||
opts := []graphql.SchemaOpt{graphql.UseFieldResolvers()}
|
|
||||||
schema := graphql.MustParseSchema(s, &query{}, opts...)
|
|
||||||
```
|
|
||||||
|
|
||||||
When using `UseFieldResolvers` schema option, a struct field will be used *only* when:
|
|
||||||
- there is no method for a struct field
|
|
||||||
- a struct field does not implement an interface method
|
|
||||||
- a struct field does not have arguments
|
|
||||||
|
|
||||||
The method has up to two arguments:
|
|
||||||
|
|
||||||
- Optional `context.Context` argument.
|
|
||||||
- Mandatory `*struct { ... }` argument if the corresponding GraphQL field has arguments. The names of the struct fields have to be [exported](https://golang.org/ref/spec#Exported_identifiers) and have to match the names of the GraphQL arguments in a non-case-sensitive way.
|
|
||||||
|
|
||||||
The method has up to two results:
|
|
||||||
|
|
||||||
- The GraphQL field's value as determined by the resolver.
|
|
||||||
- Optional `error` result.
|
|
||||||
|
|
||||||
Example for a simple resolver method:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (r *helloWorldResolver) Hello() string {
|
|
||||||
return "Hello world!"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The following signature is also allowed:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (r *helloWorldResolver) Hello(ctx context.Context) (string, error) {
|
|
||||||
return "Hello world!", nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Schema Options
|
|
||||||
|
|
||||||
- `UseStringDescriptions()` enables the usage of double quoted and triple quoted. When this is not enabled, comments are parsed as descriptions instead.
|
|
||||||
- `UseFieldResolvers()` specifies whether to use struct field resolvers.
|
|
||||||
- `MaxDepth(n int)` specifies the maximum field nesting depth in a query. The default is 0 which disables max depth checking.
|
|
||||||
- `MaxParallelism(n int)` specifies the maximum number of resolvers per request allowed to run in parallel. The default is 10.
|
|
||||||
- `Tracer(tracer trace.Tracer)` is used to trace queries and fields. It defaults to `trace.OpenTracingTracer`.
|
|
||||||
- `ValidationTracer(tracer trace.ValidationTracer)` is used to trace validation errors. It defaults to `trace.NoopValidationTracer`.
|
|
||||||
- `Logger(logger log.Logger)` is used to log panics during query execution. It defaults to `exec.DefaultLogger`.
|
|
||||||
- `PanicHandler(panicHandler errors.PanicHandler)` is used to transform panics into errors during query execution. It defaults to `errors.DefaultPanicHandler`.
|
|
||||||
- `DisableIntrospection()` disables introspection queries.
|
|
||||||
|
|
||||||
### Custom Errors
|
|
||||||
|
|
||||||
Errors returned by resolvers can include custom extensions by implementing the `ResolverError` interface:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type ResolverError interface {
|
|
||||||
error
|
|
||||||
Extensions() map[string]interface{}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Example of a simple custom error:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type droidNotFoundError struct {
|
|
||||||
Code string `json:"code"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e droidNotFoundError) Error() string {
|
|
||||||
return fmt.Sprintf("error [%s]: %s", e.Code, e.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e droidNotFoundError) Extensions() map[string]interface{} {
|
|
||||||
return map[string]interface{}{
|
|
||||||
"code": e.Code,
|
|
||||||
"message": e.Message,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Which could produce a GraphQL error such as:
|
|
||||||
|
|
||||||
```go
|
|
||||||
{
|
|
||||||
"errors": [
|
|
||||||
{
|
|
||||||
"message": "error [NotFound]: This is not the droid you are looking for",
|
|
||||||
"path": [
|
|
||||||
"droid"
|
|
||||||
],
|
|
||||||
"extensions": {
|
|
||||||
"code": "NotFound",
|
|
||||||
"message": "This is not the droid you are looking for"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"data": null
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### [Examples](https://github.com/graph-gophers/graphql-go/wiki/Examples)
|
|
||||||
|
|
||||||
### [Companies that use this library](https://github.com/graph-gophers/graphql-go/wiki/Users)
|
|
13
vendor/github.com/graph-gophers/graphql-go/decode/decode.go
generated
vendored
13
vendor/github.com/graph-gophers/graphql-go/decode/decode.go
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
package decode
|
|
||||||
|
|
||||||
// Unmarshaler defines the api of Go types mapped to custom GraphQL scalar types
|
|
||||||
type Unmarshaler interface {
|
|
||||||
// ImplementsGraphQLType maps the implementing custom Go type
|
|
||||||
// to the GraphQL scalar type in the schema.
|
|
||||||
ImplementsGraphQLType(name string) bool
|
|
||||||
// UnmarshalGraphQL is the custom unmarshaler for the implementing type
|
|
||||||
//
|
|
||||||
// This function will be called whenever you use the
|
|
||||||
// custom GraphQL scalar type as an input
|
|
||||||
UnmarshalGraphQL(input interface{}) error
|
|
||||||
}
|
|
59
vendor/github.com/graph-gophers/graphql-go/errors/errors.go
generated
vendored
59
vendor/github.com/graph-gophers/graphql-go/errors/errors.go
generated
vendored
@ -1,59 +0,0 @@
|
|||||||
package errors
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type QueryError struct {
|
|
||||||
Err error `json:"-"` // Err holds underlying if available
|
|
||||||
Message string `json:"message"`
|
|
||||||
Locations []Location `json:"locations,omitempty"`
|
|
||||||
Path []interface{} `json:"path,omitempty"`
|
|
||||||
Rule string `json:"-"`
|
|
||||||
ResolverError error `json:"-"`
|
|
||||||
Extensions map[string]interface{} `json:"extensions,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Location struct {
|
|
||||||
Line int `json:"line"`
|
|
||||||
Column int `json:"column"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a Location) Before(b Location) bool {
|
|
||||||
return a.Line < b.Line || (a.Line == b.Line && a.Column < b.Column)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Errorf(format string, a ...interface{}) *QueryError {
|
|
||||||
// similar to fmt.Errorf, Errorf will wrap the last argument if it is an instance of error
|
|
||||||
var err error
|
|
||||||
if n := len(a); n > 0 {
|
|
||||||
if v, ok := a[n-1].(error); ok {
|
|
||||||
err = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &QueryError{
|
|
||||||
Err: err,
|
|
||||||
Message: fmt.Sprintf(format, a...),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err *QueryError) Error() string {
|
|
||||||
if err == nil {
|
|
||||||
return "<nil>"
|
|
||||||
}
|
|
||||||
str := fmt.Sprintf("graphql: %s", err.Message)
|
|
||||||
for _, loc := range err.Locations {
|
|
||||||
str += fmt.Sprintf(" (line %d, column %d)", loc.Line, loc.Column)
|
|
||||||
}
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err *QueryError) Unwrap() error {
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ error = &QueryError{}
|
|
18
vendor/github.com/graph-gophers/graphql-go/errors/panic_handler.go
generated
vendored
18
vendor/github.com/graph-gophers/graphql-go/errors/panic_handler.go
generated
vendored
@ -1,18 +0,0 @@
|
|||||||
package errors
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PanicHandler is the interface used to create custom panic errors that occur during query execution
|
|
||||||
type PanicHandler interface {
|
|
||||||
MakePanicError(ctx context.Context, value interface{}) *QueryError
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultPanicHandler is the default PanicHandler
|
|
||||||
type DefaultPanicHandler struct{}
|
|
||||||
|
|
||||||
// MakePanicError creates a new QueryError from a panic that occurred during execution
|
|
||||||
func (h *DefaultPanicHandler) MakePanicError(ctx context.Context, value interface{}) *QueryError {
|
|
||||||
return Errorf("panic occurred: %v", value)
|
|
||||||
}
|
|
339
vendor/github.com/graph-gophers/graphql-go/graphql.go
generated
vendored
339
vendor/github.com/graph-gophers/graphql-go/graphql.go
generated
vendored
@ -1,339 +0,0 @@
|
|||||||
package graphql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/graph-gophers/graphql-go/errors"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/common"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/exec"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/exec/resolvable"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/exec/selected"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/query"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/schema"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/validation"
|
|
||||||
"github.com/graph-gophers/graphql-go/introspection"
|
|
||||||
"github.com/graph-gophers/graphql-go/log"
|
|
||||||
"github.com/graph-gophers/graphql-go/trace"
|
|
||||||
"github.com/graph-gophers/graphql-go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ParseSchema parses a GraphQL schema and attaches the given root resolver. It returns an error if
|
|
||||||
// the Go type signature of the resolvers does not match the schema. If nil is passed as the
|
|
||||||
// resolver, then the schema can not be executed, but it may be inspected (e.g. with ToJSON).
|
|
||||||
func ParseSchema(schemaString string, resolver interface{}, opts ...SchemaOpt) (*Schema, error) {
|
|
||||||
s := &Schema{
|
|
||||||
schema: schema.New(),
|
|
||||||
maxParallelism: 10,
|
|
||||||
tracer: trace.OpenTracingTracer{},
|
|
||||||
logger: &log.DefaultLogger{},
|
|
||||||
panicHandler: &errors.DefaultPanicHandler{},
|
|
||||||
}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.validationTracer == nil {
|
|
||||||
if tracer, ok := s.tracer.(trace.ValidationTracerContext); ok {
|
|
||||||
s.validationTracer = tracer
|
|
||||||
} else {
|
|
||||||
s.validationTracer = &validationBridgingTracer{tracer: trace.NoopValidationTracer{}}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := schema.Parse(s.schema, schemaString, s.useStringDescriptions); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := s.validateSchema(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := resolvable.ApplyResolver(s.schema, resolver)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
s.res = r
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustParseSchema calls ParseSchema and panics on error.
|
|
||||||
func MustParseSchema(schemaString string, resolver interface{}, opts ...SchemaOpt) *Schema {
|
|
||||||
s, err := ParseSchema(schemaString, resolver, opts...)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Schema represents a GraphQL schema with an optional resolver.
|
|
||||||
type Schema struct {
|
|
||||||
schema *types.Schema
|
|
||||||
res *resolvable.Schema
|
|
||||||
|
|
||||||
maxDepth int
|
|
||||||
maxParallelism int
|
|
||||||
tracer trace.Tracer
|
|
||||||
validationTracer trace.ValidationTracerContext
|
|
||||||
logger log.Logger
|
|
||||||
panicHandler errors.PanicHandler
|
|
||||||
useStringDescriptions bool
|
|
||||||
disableIntrospection bool
|
|
||||||
subscribeResolverTimeout time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Schema) ASTSchema() *types.Schema {
|
|
||||||
return s.schema
|
|
||||||
}
|
|
||||||
|
|
||||||
// SchemaOpt is an option to pass to ParseSchema or MustParseSchema.
|
|
||||||
type SchemaOpt func(*Schema)
|
|
||||||
|
|
||||||
// UseStringDescriptions enables the usage of double quoted and triple quoted
|
|
||||||
// strings as descriptions as per the June 2018 spec
|
|
||||||
// https://facebook.github.io/graphql/June2018/. When this is not enabled,
|
|
||||||
// comments are parsed as descriptions instead.
|
|
||||||
func UseStringDescriptions() SchemaOpt {
|
|
||||||
return func(s *Schema) {
|
|
||||||
s.useStringDescriptions = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UseFieldResolvers specifies whether to use struct field resolvers
|
|
||||||
func UseFieldResolvers() SchemaOpt {
|
|
||||||
return func(s *Schema) {
|
|
||||||
s.schema.UseFieldResolvers = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MaxDepth specifies the maximum field nesting depth in a query. The default is 0 which disables max depth checking.
|
|
||||||
func MaxDepth(n int) SchemaOpt {
|
|
||||||
return func(s *Schema) {
|
|
||||||
s.maxDepth = n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MaxParallelism specifies the maximum number of resolvers per request allowed to run in parallel. The default is 10.
|
|
||||||
func MaxParallelism(n int) SchemaOpt {
|
|
||||||
return func(s *Schema) {
|
|
||||||
s.maxParallelism = n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tracer is used to trace queries and fields. It defaults to trace.OpenTracingTracer.
|
|
||||||
func Tracer(tracer trace.Tracer) SchemaOpt {
|
|
||||||
return func(s *Schema) {
|
|
||||||
s.tracer = tracer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidationTracer is used to trace validation errors. It defaults to trace.NoopValidationTracer.
|
|
||||||
// Deprecated: context is needed to support tracing correctly. Use a Tracer which implements trace.ValidationTracerContext.
|
|
||||||
func ValidationTracer(tracer trace.ValidationTracer) SchemaOpt { //nolint:staticcheck
|
|
||||||
return func(s *Schema) {
|
|
||||||
s.validationTracer = &validationBridgingTracer{tracer: tracer}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logger is used to log panics during query execution. It defaults to exec.DefaultLogger.
|
|
||||||
func Logger(logger log.Logger) SchemaOpt {
|
|
||||||
return func(s *Schema) {
|
|
||||||
s.logger = logger
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PanicHandler is used to customize the panic errors during query execution.
|
|
||||||
// It defaults to errors.DefaultPanicHandler.
|
|
||||||
func PanicHandler(panicHandler errors.PanicHandler) SchemaOpt {
|
|
||||||
return func(s *Schema) {
|
|
||||||
s.panicHandler = panicHandler
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisableIntrospection disables introspection queries.
|
|
||||||
func DisableIntrospection() SchemaOpt {
|
|
||||||
return func(s *Schema) {
|
|
||||||
s.disableIntrospection = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubscribeResolverTimeout is an option to control the amount of time
|
|
||||||
// we allow for a single subscribe message resolver to complete it's job
|
|
||||||
// before it times out and returns an error to the subscriber.
|
|
||||||
func SubscribeResolverTimeout(timeout time.Duration) SchemaOpt {
|
|
||||||
return func(s *Schema) {
|
|
||||||
s.subscribeResolverTimeout = timeout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Response represents a typical response of a GraphQL server. It may be encoded to JSON directly or
|
|
||||||
// it may be further processed to a custom response type, for example to include custom error data.
|
|
||||||
// Errors are intentionally serialized first based on the advice in https://github.com/facebook/graphql/commit/7b40390d48680b15cb93e02d46ac5eb249689876#diff-757cea6edf0288677a9eea4cfc801d87R107
|
|
||||||
type Response struct {
|
|
||||||
Errors []*errors.QueryError `json:"errors,omitempty"`
|
|
||||||
Data json.RawMessage `json:"data,omitempty"`
|
|
||||||
Extensions map[string]interface{} `json:"extensions,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the given query with the schema.
|
|
||||||
func (s *Schema) Validate(queryString string) []*errors.QueryError {
|
|
||||||
return s.ValidateWithVariables(queryString, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateWithVariables validates the given query with the schema and the input variables.
|
|
||||||
func (s *Schema) ValidateWithVariables(queryString string, variables map[string]interface{}) []*errors.QueryError {
|
|
||||||
doc, qErr := query.Parse(queryString)
|
|
||||||
if qErr != nil {
|
|
||||||
return []*errors.QueryError{qErr}
|
|
||||||
}
|
|
||||||
|
|
||||||
return validation.Validate(s.schema, doc, variables, s.maxDepth)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exec executes the given query with the schema's resolver. It panics if the schema was created
|
|
||||||
// without a resolver. If the context get cancelled, no further resolvers will be called and a
|
|
||||||
// the context error will be returned as soon as possible (not immediately).
|
|
||||||
func (s *Schema) Exec(ctx context.Context, queryString string, operationName string, variables map[string]interface{}) *Response {
|
|
||||||
if !s.res.Resolver.IsValid() {
|
|
||||||
panic("schema created without resolver, can not exec")
|
|
||||||
}
|
|
||||||
return s.exec(ctx, queryString, operationName, variables, s.res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Schema) exec(ctx context.Context, queryString string, operationName string, variables map[string]interface{}, res *resolvable.Schema) *Response {
|
|
||||||
doc, qErr := query.Parse(queryString)
|
|
||||||
if qErr != nil {
|
|
||||||
return &Response{Errors: []*errors.QueryError{qErr}}
|
|
||||||
}
|
|
||||||
|
|
||||||
validationFinish := s.validationTracer.TraceValidation(ctx)
|
|
||||||
errs := validation.Validate(s.schema, doc, variables, s.maxDepth)
|
|
||||||
validationFinish(errs)
|
|
||||||
if len(errs) != 0 {
|
|
||||||
return &Response{Errors: errs}
|
|
||||||
}
|
|
||||||
|
|
||||||
op, err := getOperation(doc, operationName)
|
|
||||||
if err != nil {
|
|
||||||
return &Response{Errors: []*errors.QueryError{errors.Errorf("%s", err)}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the optional "operationName" POST parameter is not provided then
|
|
||||||
// use the query's operation name for improved tracing.
|
|
||||||
if operationName == "" {
|
|
||||||
operationName = op.Name.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscriptions are not valid in Exec. Use schema.Subscribe() instead.
|
|
||||||
if op.Type == query.Subscription {
|
|
||||||
return &Response{Errors: []*errors.QueryError{{Message: "graphql-ws protocol header is missing"}}}
|
|
||||||
}
|
|
||||||
if op.Type == query.Mutation {
|
|
||||||
if _, ok := s.schema.EntryPoints["mutation"]; !ok {
|
|
||||||
return &Response{Errors: []*errors.QueryError{{Message: "no mutations are offered by the schema"}}}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill in variables with the defaults from the operation
|
|
||||||
if variables == nil {
|
|
||||||
variables = make(map[string]interface{}, len(op.Vars))
|
|
||||||
}
|
|
||||||
for _, v := range op.Vars {
|
|
||||||
if _, ok := variables[v.Name.Name]; !ok && v.Default != nil {
|
|
||||||
variables[v.Name.Name] = v.Default.Deserialize(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r := &exec.Request{
|
|
||||||
Request: selected.Request{
|
|
||||||
Doc: doc,
|
|
||||||
Vars: variables,
|
|
||||||
Schema: s.schema,
|
|
||||||
DisableIntrospection: s.disableIntrospection,
|
|
||||||
},
|
|
||||||
Limiter: make(chan struct{}, s.maxParallelism),
|
|
||||||
Tracer: s.tracer,
|
|
||||||
Logger: s.logger,
|
|
||||||
PanicHandler: s.panicHandler,
|
|
||||||
}
|
|
||||||
varTypes := make(map[string]*introspection.Type)
|
|
||||||
for _, v := range op.Vars {
|
|
||||||
t, err := common.ResolveType(v.Type, s.schema.Resolve)
|
|
||||||
if err != nil {
|
|
||||||
return &Response{Errors: []*errors.QueryError{err}}
|
|
||||||
}
|
|
||||||
varTypes[v.Name.Name] = introspection.WrapType(t)
|
|
||||||
}
|
|
||||||
traceCtx, finish := s.tracer.TraceQuery(ctx, queryString, operationName, variables, varTypes)
|
|
||||||
data, errs := r.Execute(traceCtx, res, op)
|
|
||||||
finish(errs)
|
|
||||||
|
|
||||||
return &Response{
|
|
||||||
Data: data,
|
|
||||||
Errors: errs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Schema) validateSchema() error {
|
|
||||||
// https://graphql.github.io/graphql-spec/June2018/#sec-Root-Operation-Types
|
|
||||||
// > The query root operation type must be provided and must be an Object type.
|
|
||||||
if err := validateRootOp(s.schema, "query", true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// > The mutation root operation type is optional; if it is not provided, the service does not support mutations.
|
|
||||||
// > If it is provided, it must be an Object type.
|
|
||||||
if err := validateRootOp(s.schema, "mutation", false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// > Similarly, the subscription root operation type is also optional; if it is not provided, the service does not
|
|
||||||
// > support subscriptions. If it is provided, it must be an Object type.
|
|
||||||
if err := validateRootOp(s.schema, "subscription", false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type validationBridgingTracer struct {
|
|
||||||
tracer trace.ValidationTracer //nolint:staticcheck
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *validationBridgingTracer) TraceValidation(context.Context) trace.TraceValidationFinishFunc {
|
|
||||||
return t.tracer.TraceValidation()
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateRootOp(s *types.Schema, name string, mandatory bool) error {
|
|
||||||
t, ok := s.EntryPoints[name]
|
|
||||||
if !ok {
|
|
||||||
if mandatory {
|
|
||||||
return fmt.Errorf("root operation %q must be defined", name)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if t.Kind() != "OBJECT" {
|
|
||||||
return fmt.Errorf("root operation %q must be an OBJECT", name)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getOperation(document *types.ExecutableDefinition, operationName string) (*types.OperationDefinition, error) {
|
|
||||||
if len(document.Operations) == 0 {
|
|
||||||
return nil, fmt.Errorf("no operations in query document")
|
|
||||||
}
|
|
||||||
|
|
||||||
if operationName == "" {
|
|
||||||
if len(document.Operations) > 1 {
|
|
||||||
return nil, fmt.Errorf("more than one operation in query document and no operation name given")
|
|
||||||
}
|
|
||||||
for _, op := range document.Operations {
|
|
||||||
return op, nil // return the one and only operation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
op := document.Operations.Get(operationName)
|
|
||||||
if op == nil {
|
|
||||||
return nil, fmt.Errorf("no operation with name %q", operationName)
|
|
||||||
}
|
|
||||||
return op, nil
|
|
||||||
}
|
|
30
vendor/github.com/graph-gophers/graphql-go/id.go
generated
vendored
30
vendor/github.com/graph-gophers/graphql-go/id.go
generated
vendored
@ -1,30 +0,0 @@
|
|||||||
package graphql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ID represents GraphQL's "ID" scalar type. A custom type may be used instead.
|
|
||||||
type ID string
|
|
||||||
|
|
||||||
func (ID) ImplementsGraphQLType(name string) bool {
|
|
||||||
return name == "ID"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id *ID) UnmarshalGraphQL(input interface{}) error {
|
|
||||||
var err error
|
|
||||||
switch input := input.(type) {
|
|
||||||
case string:
|
|
||||||
*id = ID(input)
|
|
||||||
case int32:
|
|
||||||
*id = ID(strconv.Itoa(int(input)))
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf("wrong type for ID: %T", input)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id ID) MarshalJSON() ([]byte, error) {
|
|
||||||
return strconv.AppendQuote(nil, string(id)), nil
|
|
||||||
}
|
|
103
vendor/github.com/graph-gophers/graphql-go/internal/common/blockstring.go
generated
vendored
103
vendor/github.com/graph-gophers/graphql-go/internal/common/blockstring.go
generated
vendored
@ -1,103 +0,0 @@
|
|||||||
// MIT License
|
|
||||||
//
|
|
||||||
// Copyright (c) 2019 GraphQL Contributors
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
//
|
|
||||||
// This implementation has been adapted from the graphql-js reference implementation
|
|
||||||
// https://github.com/graphql/graphql-js/blob/5eb7c4ded7ceb83ac742149cbe0dae07a8af9a30/src/language/blockString.js
|
|
||||||
// which is released under the MIT License above.
|
|
||||||
|
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Produces the value of a block string from its parsed raw value, similar to
|
|
||||||
// CoffeeScript's block string, Python's docstring trim or Ruby's strip_heredoc.
|
|
||||||
//
|
|
||||||
// This implements the GraphQL spec's BlockStringValue() static algorithm.
|
|
||||||
func blockString(raw string) string {
|
|
||||||
lines := strings.Split(raw, "\n")
|
|
||||||
|
|
||||||
// Remove common indentation from all lines except the first (which has none)
|
|
||||||
ind := blockStringIndentation(lines)
|
|
||||||
if ind > 0 {
|
|
||||||
for i := 1; i < len(lines); i++ {
|
|
||||||
l := lines[i]
|
|
||||||
if len(l) < ind {
|
|
||||||
lines[i] = ""
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
lines[i] = l[ind:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove leading and trailing blank lines
|
|
||||||
trimStart := 0
|
|
||||||
for i := 0; i < len(lines) && isBlank(lines[i]); i++ {
|
|
||||||
trimStart++
|
|
||||||
}
|
|
||||||
lines = lines[trimStart:]
|
|
||||||
trimEnd := 0
|
|
||||||
for i := len(lines) - 1; i > 0 && isBlank(lines[i]); i-- {
|
|
||||||
trimEnd++
|
|
||||||
}
|
|
||||||
lines = lines[:len(lines)-trimEnd]
|
|
||||||
|
|
||||||
return strings.Join(lines, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func blockStringIndentation(lines []string) int {
|
|
||||||
var commonIndent *int
|
|
||||||
for i := 1; i < len(lines); i++ {
|
|
||||||
l := lines[i]
|
|
||||||
indent := leadingWhitespace(l)
|
|
||||||
if indent == len(l) {
|
|
||||||
// don't consider blank/empty lines
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if indent == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if commonIndent == nil || indent < *commonIndent {
|
|
||||||
commonIndent = &indent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if commonIndent == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return *commonIndent
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBlank(s string) bool {
|
|
||||||
return len(s) == 0 || leadingWhitespace(s) == len(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func leadingWhitespace(s string) int {
|
|
||||||
i := 0
|
|
||||||
for _, r := range s {
|
|
||||||
if r != '\t' && r != ' ' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
18
vendor/github.com/graph-gophers/graphql-go/internal/common/directive.go
generated
vendored
18
vendor/github.com/graph-gophers/graphql-go/internal/common/directive.go
generated
vendored
@ -1,18 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import "github.com/graph-gophers/graphql-go/types"
|
|
||||||
|
|
||||||
func ParseDirectives(l *Lexer) types.DirectiveList {
|
|
||||||
var directives types.DirectiveList
|
|
||||||
for l.Peek() == '@' {
|
|
||||||
l.ConsumeToken('@')
|
|
||||||
d := &types.Directive{}
|
|
||||||
d.Name = l.ConsumeIdentWithLoc()
|
|
||||||
d.Name.Loc.Column--
|
|
||||||
if l.Peek() == '(' {
|
|
||||||
d.Arguments = ParseArgumentList(l)
|
|
||||||
}
|
|
||||||
directives = append(directives, d)
|
|
||||||
}
|
|
||||||
return directives
|
|
||||||
}
|
|
229
vendor/github.com/graph-gophers/graphql-go/internal/common/lexer.go
generated
vendored
229
vendor/github.com/graph-gophers/graphql-go/internal/common/lexer.go
generated
vendored
@ -1,229 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"text/scanner"
|
|
||||||
|
|
||||||
"github.com/graph-gophers/graphql-go/errors"
|
|
||||||
"github.com/graph-gophers/graphql-go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type syntaxError string
|
|
||||||
|
|
||||||
type Lexer struct {
|
|
||||||
sc *scanner.Scanner
|
|
||||||
next rune
|
|
||||||
comment bytes.Buffer
|
|
||||||
useStringDescriptions bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type Ident struct {
|
|
||||||
Name string
|
|
||||||
Loc errors.Location
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLexer(s string, useStringDescriptions bool) *Lexer {
|
|
||||||
sc := &scanner.Scanner{
|
|
||||||
Mode: scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings,
|
|
||||||
}
|
|
||||||
sc.Init(strings.NewReader(s))
|
|
||||||
|
|
||||||
l := Lexer{sc: sc, useStringDescriptions: useStringDescriptions}
|
|
||||||
l.sc.Error = l.CatchScannerError
|
|
||||||
|
|
||||||
return &l
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Lexer) CatchSyntaxError(f func()) (errRes *errors.QueryError) {
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
if err, ok := err.(syntaxError); ok {
|
|
||||||
errRes = errors.Errorf("syntax error: %s", err)
|
|
||||||
errRes.Locations = []errors.Location{l.Location()}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
f()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Lexer) Peek() rune {
|
|
||||||
return l.next
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConsumeWhitespace consumes whitespace and tokens equivalent to whitespace (e.g. commas and comments).
|
|
||||||
//
|
|
||||||
// Consumed comment characters will build the description for the next type or field encountered.
|
|
||||||
// The description is available from `DescComment()`, and will be reset every time `ConsumeWhitespace()` is
|
|
||||||
// executed unless l.useStringDescriptions is set.
|
|
||||||
func (l *Lexer) ConsumeWhitespace() {
|
|
||||||
l.comment.Reset()
|
|
||||||
for {
|
|
||||||
l.next = l.sc.Scan()
|
|
||||||
|
|
||||||
if l.next == ',' {
|
|
||||||
// Similar to white space and line terminators, commas (',') are used to improve the
|
|
||||||
// legibility of source text and separate lexical tokens but are otherwise syntactically and
|
|
||||||
// semantically insignificant within GraphQL documents.
|
|
||||||
//
|
|
||||||
// http://facebook.github.io/graphql/draft/#sec-Insignificant-Commas
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if l.next == '#' {
|
|
||||||
// GraphQL source documents may contain single-line comments, starting with the '#' marker.
|
|
||||||
//
|
|
||||||
// A comment can contain any Unicode code point except `LineTerminator` so a comment always
|
|
||||||
// consists of all code points starting with the '#' character up to but not including the
|
|
||||||
// line terminator.
|
|
||||||
l.consumeComment()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// consumeDescription optionally consumes a description based on the June 2018 graphql spec if any are present.
|
|
||||||
//
|
|
||||||
// Single quote strings are also single line. Triple quote strings can be multi-line. Triple quote strings
|
|
||||||
// whitespace trimmed on both ends.
|
|
||||||
// If a description is found, consume any following comments as well
|
|
||||||
//
|
|
||||||
// http://facebook.github.io/graphql/June2018/#sec-Descriptions
|
|
||||||
func (l *Lexer) consumeDescription() string {
|
|
||||||
// If the next token is not a string, we don't consume it
|
|
||||||
if l.next != scanner.String {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
// Triple quote string is an empty "string" followed by an open quote due to the way the parser treats strings as one token
|
|
||||||
var desc string
|
|
||||||
if l.sc.Peek() == '"' {
|
|
||||||
desc = l.consumeTripleQuoteComment()
|
|
||||||
} else {
|
|
||||||
desc = l.consumeStringComment()
|
|
||||||
}
|
|
||||||
l.ConsumeWhitespace()
|
|
||||||
return desc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Lexer) ConsumeIdent() string {
|
|
||||||
name := l.sc.TokenText()
|
|
||||||
l.ConsumeToken(scanner.Ident)
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Lexer) ConsumeIdentWithLoc() types.Ident {
|
|
||||||
loc := l.Location()
|
|
||||||
name := l.sc.TokenText()
|
|
||||||
l.ConsumeToken(scanner.Ident)
|
|
||||||
return types.Ident{Name: name, Loc: loc}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Lexer) ConsumeKeyword(keyword string) {
|
|
||||||
if l.next != scanner.Ident || l.sc.TokenText() != keyword {
|
|
||||||
l.SyntaxError(fmt.Sprintf("unexpected %q, expecting %q", l.sc.TokenText(), keyword))
|
|
||||||
}
|
|
||||||
l.ConsumeWhitespace()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Lexer) ConsumeLiteral() *types.PrimitiveValue {
|
|
||||||
lit := &types.PrimitiveValue{Type: l.next, Text: l.sc.TokenText()}
|
|
||||||
l.ConsumeWhitespace()
|
|
||||||
return lit
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Lexer) ConsumeToken(expected rune) {
|
|
||||||
if l.next != expected {
|
|
||||||
l.SyntaxError(fmt.Sprintf("unexpected %q, expecting %s", l.sc.TokenText(), scanner.TokenString(expected)))
|
|
||||||
}
|
|
||||||
l.ConsumeWhitespace()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Lexer) DescComment() string {
|
|
||||||
comment := l.comment.String()
|
|
||||||
desc := l.consumeDescription()
|
|
||||||
if l.useStringDescriptions {
|
|
||||||
return desc
|
|
||||||
}
|
|
||||||
return comment
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Lexer) SyntaxError(message string) {
|
|
||||||
panic(syntaxError(message))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Lexer) Location() errors.Location {
|
|
||||||
return errors.Location{
|
|
||||||
Line: l.sc.Line,
|
|
||||||
Column: l.sc.Column,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Lexer) consumeTripleQuoteComment() string {
|
|
||||||
l.next = l.sc.Next()
|
|
||||||
if l.next != '"' {
|
|
||||||
panic("consumeTripleQuoteComment used in wrong context: no third quote?")
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
var numQuotes int
|
|
||||||
for {
|
|
||||||
l.next = l.sc.Next()
|
|
||||||
if l.next == '"' {
|
|
||||||
numQuotes++
|
|
||||||
} else {
|
|
||||||
numQuotes = 0
|
|
||||||
}
|
|
||||||
buf.WriteRune(l.next)
|
|
||||||
if numQuotes == 3 || l.next == scanner.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val := buf.String()
|
|
||||||
val = val[:len(val)-numQuotes]
|
|
||||||
return blockString(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Lexer) consumeStringComment() string {
|
|
||||||
val, err := strconv.Unquote(l.sc.TokenText())
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
// consumeComment consumes all characters from `#` to the first encountered line terminator.
|
|
||||||
// The characters are appended to `l.comment`.
|
|
||||||
func (l *Lexer) consumeComment() {
|
|
||||||
if l.next != '#' {
|
|
||||||
panic("consumeComment used in wrong context")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: count and trim whitespace so we can dedent any following lines.
|
|
||||||
if l.sc.Peek() == ' ' {
|
|
||||||
l.sc.Next()
|
|
||||||
}
|
|
||||||
|
|
||||||
if l.comment.Len() > 0 {
|
|
||||||
l.comment.WriteRune('\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
next := l.sc.Next()
|
|
||||||
if next == '\r' || next == '\n' || next == scanner.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
l.comment.WriteRune(next)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Lexer) CatchScannerError(s *scanner.Scanner, msg string) {
|
|
||||||
l.SyntaxError(msg)
|
|
||||||
}
|
|
58
vendor/github.com/graph-gophers/graphql-go/internal/common/literals.go
generated
vendored
58
vendor/github.com/graph-gophers/graphql-go/internal/common/literals.go
generated
vendored
@ -1,58 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"text/scanner"
|
|
||||||
|
|
||||||
"github.com/graph-gophers/graphql-go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ParseLiteral(l *Lexer, constOnly bool) types.Value {
|
|
||||||
loc := l.Location()
|
|
||||||
switch l.Peek() {
|
|
||||||
case '$':
|
|
||||||
if constOnly {
|
|
||||||
l.SyntaxError("variable not allowed")
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
l.ConsumeToken('$')
|
|
||||||
return &types.Variable{Name: l.ConsumeIdent(), Loc: loc}
|
|
||||||
|
|
||||||
case scanner.Int, scanner.Float, scanner.String, scanner.Ident:
|
|
||||||
lit := l.ConsumeLiteral()
|
|
||||||
if lit.Type == scanner.Ident && lit.Text == "null" {
|
|
||||||
return &types.NullValue{Loc: loc}
|
|
||||||
}
|
|
||||||
lit.Loc = loc
|
|
||||||
return lit
|
|
||||||
case '-':
|
|
||||||
l.ConsumeToken('-')
|
|
||||||
lit := l.ConsumeLiteral()
|
|
||||||
lit.Text = "-" + lit.Text
|
|
||||||
lit.Loc = loc
|
|
||||||
return lit
|
|
||||||
case '[':
|
|
||||||
l.ConsumeToken('[')
|
|
||||||
var list []types.Value
|
|
||||||
for l.Peek() != ']' {
|
|
||||||
list = append(list, ParseLiteral(l, constOnly))
|
|
||||||
}
|
|
||||||
l.ConsumeToken(']')
|
|
||||||
return &types.ListValue{Values: list, Loc: loc}
|
|
||||||
|
|
||||||
case '{':
|
|
||||||
l.ConsumeToken('{')
|
|
||||||
var fields []*types.ObjectField
|
|
||||||
for l.Peek() != '}' {
|
|
||||||
name := l.ConsumeIdentWithLoc()
|
|
||||||
l.ConsumeToken(':')
|
|
||||||
value := ParseLiteral(l, constOnly)
|
|
||||||
fields = append(fields, &types.ObjectField{Name: name, Value: value})
|
|
||||||
}
|
|
||||||
l.ConsumeToken('}')
|
|
||||||
return &types.ObjectValue{Fields: fields, Loc: loc}
|
|
||||||
|
|
||||||
default:
|
|
||||||
l.SyntaxError("invalid value")
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
67
vendor/github.com/graph-gophers/graphql-go/internal/common/types.go
generated
vendored
67
vendor/github.com/graph-gophers/graphql-go/internal/common/types.go
generated
vendored
@ -1,67 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/graph-gophers/graphql-go/errors"
|
|
||||||
"github.com/graph-gophers/graphql-go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ParseType(l *Lexer) types.Type {
|
|
||||||
t := parseNullType(l)
|
|
||||||
if l.Peek() == '!' {
|
|
||||||
l.ConsumeToken('!')
|
|
||||||
return &types.NonNull{OfType: t}
|
|
||||||
}
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseNullType(l *Lexer) types.Type {
|
|
||||||
if l.Peek() == '[' {
|
|
||||||
l.ConsumeToken('[')
|
|
||||||
ofType := ParseType(l)
|
|
||||||
l.ConsumeToken(']')
|
|
||||||
return &types.List{OfType: ofType}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &types.TypeName{Ident: l.ConsumeIdentWithLoc()}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Resolver func(name string) types.Type
|
|
||||||
|
|
||||||
// ResolveType attempts to resolve a type's name against a resolving function.
|
|
||||||
// This function is used when one needs to check if a TypeName exists in the resolver (typically a Schema).
|
|
||||||
//
|
|
||||||
// In the example below, ResolveType would be used to check if the resolving function
|
|
||||||
// returns a valid type for Dimension:
|
|
||||||
//
|
|
||||||
// type Profile {
|
|
||||||
// picture(dimensions: Dimension): Url
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// ResolveType recursively unwraps List and NonNull types until a NamedType is reached.
|
|
||||||
func ResolveType(t types.Type, resolver Resolver) (types.Type, *errors.QueryError) {
|
|
||||||
switch t := t.(type) {
|
|
||||||
case *types.List:
|
|
||||||
ofType, err := ResolveType(t.OfType, resolver)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &types.List{OfType: ofType}, nil
|
|
||||||
case *types.NonNull:
|
|
||||||
ofType, err := ResolveType(t.OfType, resolver)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &types.NonNull{OfType: ofType}, nil
|
|
||||||
case *types.TypeName:
|
|
||||||
refT := resolver(t.Name)
|
|
||||||
if refT == nil {
|
|
||||||
err := errors.Errorf("Unknown type %q.", t.Name)
|
|
||||||
err.Rule = "KnownTypeNames"
|
|
||||||
err.Locations = []errors.Location{t.Loc}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return refT, nil
|
|
||||||
default:
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
}
|
|
37
vendor/github.com/graph-gophers/graphql-go/internal/common/values.go
generated
vendored
37
vendor/github.com/graph-gophers/graphql-go/internal/common/values.go
generated
vendored
@ -1,37 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/graph-gophers/graphql-go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ParseInputValue(l *Lexer) *types.InputValueDefinition {
|
|
||||||
p := &types.InputValueDefinition{}
|
|
||||||
p.Loc = l.Location()
|
|
||||||
p.Desc = l.DescComment()
|
|
||||||
p.Name = l.ConsumeIdentWithLoc()
|
|
||||||
l.ConsumeToken(':')
|
|
||||||
p.TypeLoc = l.Location()
|
|
||||||
p.Type = ParseType(l)
|
|
||||||
if l.Peek() == '=' {
|
|
||||||
l.ConsumeToken('=')
|
|
||||||
p.Default = ParseLiteral(l, true)
|
|
||||||
}
|
|
||||||
p.Directives = ParseDirectives(l)
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseArgumentList(l *Lexer) types.ArgumentList {
|
|
||||||
var args types.ArgumentList
|
|
||||||
l.ConsumeToken('(')
|
|
||||||
for l.Peek() != ')' {
|
|
||||||
name := l.ConsumeIdentWithLoc()
|
|
||||||
l.ConsumeToken(':')
|
|
||||||
value := ParseLiteral(l, false)
|
|
||||||
args = append(args, &types.Argument{
|
|
||||||
Name: name,
|
|
||||||
Value: value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
l.ConsumeToken(')')
|
|
||||||
return args
|
|
||||||
}
|
|
381
vendor/github.com/graph-gophers/graphql-go/internal/exec/exec.go
generated
vendored
381
vendor/github.com/graph-gophers/graphql-go/internal/exec/exec.go
generated
vendored
@ -1,381 +0,0 @@
|
|||||||
package exec
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/graph-gophers/graphql-go/errors"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/exec/resolvable"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/exec/selected"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/query"
|
|
||||||
"github.com/graph-gophers/graphql-go/log"
|
|
||||||
"github.com/graph-gophers/graphql-go/trace"
|
|
||||||
"github.com/graph-gophers/graphql-go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Request struct {
|
|
||||||
selected.Request
|
|
||||||
Limiter chan struct{}
|
|
||||||
Tracer trace.Tracer
|
|
||||||
Logger log.Logger
|
|
||||||
PanicHandler errors.PanicHandler
|
|
||||||
SubscribeResolverTimeout time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Request) handlePanic(ctx context.Context) {
|
|
||||||
if value := recover(); value != nil {
|
|
||||||
r.Logger.LogPanic(ctx, value)
|
|
||||||
r.AddError(r.PanicHandler.MakePanicError(ctx, value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type extensionser interface {
|
|
||||||
Extensions() map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Request) Execute(ctx context.Context, s *resolvable.Schema, op *types.OperationDefinition) ([]byte, []*errors.QueryError) {
|
|
||||||
var out bytes.Buffer
|
|
||||||
func() {
|
|
||||||
defer r.handlePanic(ctx)
|
|
||||||
sels := selected.ApplyOperation(&r.Request, s, op)
|
|
||||||
r.execSelections(ctx, sels, nil, s, s.Resolver, &out, op.Type == query.Mutation)
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := ctx.Err(); err != nil {
|
|
||||||
return nil, []*errors.QueryError{errors.Errorf("%s", err)}
|
|
||||||
}
|
|
||||||
|
|
||||||
return out.Bytes(), r.Errs
|
|
||||||
}
|
|
||||||
|
|
||||||
type fieldToExec struct {
|
|
||||||
field *selected.SchemaField
|
|
||||||
sels []selected.Selection
|
|
||||||
resolver reflect.Value
|
|
||||||
out *bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolvedToNull(b *bytes.Buffer) bool {
|
|
||||||
return bytes.Equal(b.Bytes(), []byte("null"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Request) execSelections(ctx context.Context, sels []selected.Selection, path *pathSegment, s *resolvable.Schema, resolver reflect.Value, out *bytes.Buffer, serially bool) {
|
|
||||||
async := !serially && selected.HasAsyncSel(sels)
|
|
||||||
|
|
||||||
var fields []*fieldToExec
|
|
||||||
collectFieldsToResolve(sels, s, resolver, &fields, make(map[string]*fieldToExec))
|
|
||||||
|
|
||||||
if async {
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(len(fields))
|
|
||||||
for _, f := range fields {
|
|
||||||
go func(f *fieldToExec) {
|
|
||||||
defer wg.Done()
|
|
||||||
defer r.handlePanic(ctx)
|
|
||||||
f.out = new(bytes.Buffer)
|
|
||||||
execFieldSelection(ctx, r, s, f, &pathSegment{path, f.field.Alias}, true)
|
|
||||||
}(f)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
} else {
|
|
||||||
for _, f := range fields {
|
|
||||||
f.out = new(bytes.Buffer)
|
|
||||||
execFieldSelection(ctx, r, s, f, &pathSegment{path, f.field.Alias}, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out.WriteByte('{')
|
|
||||||
for i, f := range fields {
|
|
||||||
// If a non-nullable child resolved to null, an error was added to the
|
|
||||||
// "errors" list in the response, so this field resolves to null.
|
|
||||||
// If this field is non-nullable, the error is propagated to its parent.
|
|
||||||
if _, ok := f.field.Type.(*types.NonNull); ok && resolvedToNull(f.out) {
|
|
||||||
out.Reset()
|
|
||||||
out.Write([]byte("null"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if i > 0 {
|
|
||||||
out.WriteByte(',')
|
|
||||||
}
|
|
||||||
out.WriteByte('"')
|
|
||||||
out.WriteString(f.field.Alias)
|
|
||||||
out.WriteByte('"')
|
|
||||||
out.WriteByte(':')
|
|
||||||
out.Write(f.out.Bytes())
|
|
||||||
}
|
|
||||||
out.WriteByte('}')
|
|
||||||
}
|
|
||||||
|
|
||||||
func collectFieldsToResolve(sels []selected.Selection, s *resolvable.Schema, resolver reflect.Value, fields *[]*fieldToExec, fieldByAlias map[string]*fieldToExec) {
|
|
||||||
for _, sel := range sels {
|
|
||||||
switch sel := sel.(type) {
|
|
||||||
case *selected.SchemaField:
|
|
||||||
field, ok := fieldByAlias[sel.Alias]
|
|
||||||
if !ok { // validation already checked for conflict (TODO)
|
|
||||||
field = &fieldToExec{field: sel, resolver: resolver}
|
|
||||||
fieldByAlias[sel.Alias] = field
|
|
||||||
*fields = append(*fields, field)
|
|
||||||
}
|
|
||||||
field.sels = append(field.sels, sel.Sels...)
|
|
||||||
|
|
||||||
case *selected.TypenameField:
|
|
||||||
_, ok := fieldByAlias[sel.Alias]
|
|
||||||
if !ok {
|
|
||||||
res := reflect.ValueOf(typeOf(sel, resolver))
|
|
||||||
f := s.FieldTypename
|
|
||||||
f.TypeName = res.String()
|
|
||||||
|
|
||||||
sf := &selected.SchemaField{
|
|
||||||
Field: f,
|
|
||||||
Alias: sel.Alias,
|
|
||||||
FixedResult: res,
|
|
||||||
}
|
|
||||||
|
|
||||||
field := &fieldToExec{field: sf, resolver: resolver}
|
|
||||||
*fields = append(*fields, field)
|
|
||||||
fieldByAlias[sel.Alias] = field
|
|
||||||
}
|
|
||||||
|
|
||||||
case *selected.TypeAssertion:
|
|
||||||
out := resolver.Method(sel.MethodIndex).Call(nil)
|
|
||||||
if !out[1].Bool() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
collectFieldsToResolve(sel.Sels, s, out[0], fields, fieldByAlias)
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func typeOf(tf *selected.TypenameField, resolver reflect.Value) string {
|
|
||||||
if len(tf.TypeAssertions) == 0 {
|
|
||||||
return tf.Name
|
|
||||||
}
|
|
||||||
for name, a := range tf.TypeAssertions {
|
|
||||||
out := resolver.Method(a.MethodIndex).Call(nil)
|
|
||||||
if out[1].Bool() {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func execFieldSelection(ctx context.Context, r *Request, s *resolvable.Schema, f *fieldToExec, path *pathSegment, applyLimiter bool) {
|
|
||||||
if applyLimiter {
|
|
||||||
r.Limiter <- struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var result reflect.Value
|
|
||||||
var err *errors.QueryError
|
|
||||||
|
|
||||||
traceCtx, finish := r.Tracer.TraceField(ctx, f.field.TraceLabel, f.field.TypeName, f.field.Name, !f.field.Async, f.field.Args)
|
|
||||||
defer func() {
|
|
||||||
finish(err)
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = func() (err *errors.QueryError) {
|
|
||||||
defer func() {
|
|
||||||
if panicValue := recover(); panicValue != nil {
|
|
||||||
r.Logger.LogPanic(ctx, panicValue)
|
|
||||||
err = r.PanicHandler.MakePanicError(ctx, panicValue)
|
|
||||||
err.Path = path.toSlice()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if f.field.FixedResult.IsValid() {
|
|
||||||
result = f.field.FixedResult
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := traceCtx.Err(); err != nil {
|
|
||||||
return errors.Errorf("%s", err) // don't execute any more resolvers if context got cancelled
|
|
||||||
}
|
|
||||||
|
|
||||||
res := f.resolver
|
|
||||||
if f.field.UseMethodResolver() {
|
|
||||||
var in []reflect.Value
|
|
||||||
if f.field.HasContext {
|
|
||||||
in = append(in, reflect.ValueOf(traceCtx))
|
|
||||||
}
|
|
||||||
if f.field.ArgsPacker != nil {
|
|
||||||
in = append(in, f.field.PackedArgs)
|
|
||||||
}
|
|
||||||
callOut := res.Method(f.field.MethodIndex).Call(in)
|
|
||||||
result = callOut[0]
|
|
||||||
if f.field.HasError && !callOut[1].IsNil() {
|
|
||||||
resolverErr := callOut[1].Interface().(error)
|
|
||||||
err := errors.Errorf("%s", resolverErr)
|
|
||||||
err.Path = path.toSlice()
|
|
||||||
err.ResolverError = resolverErr
|
|
||||||
if ex, ok := callOut[1].Interface().(extensionser); ok {
|
|
||||||
err.Extensions = ex.Extensions()
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO extract out unwrapping ptr logic to a common place
|
|
||||||
if res.Kind() == reflect.Ptr {
|
|
||||||
res = res.Elem()
|
|
||||||
}
|
|
||||||
result = res.FieldByIndex(f.field.FieldIndex)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}()
|
|
||||||
|
|
||||||
if applyLimiter {
|
|
||||||
<-r.Limiter
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
// If an error occurred while resolving a field, it should be treated as though the field
|
|
||||||
// returned null, and an error must be added to the "errors" list in the response.
|
|
||||||
r.AddError(err)
|
|
||||||
f.out.WriteString("null")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
r.execSelectionSet(traceCtx, f.sels, f.field.Type, path, s, result, f.out)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Request) execSelectionSet(ctx context.Context, sels []selected.Selection, typ types.Type, path *pathSegment, s *resolvable.Schema, resolver reflect.Value, out *bytes.Buffer) {
|
|
||||||
t, nonNull := unwrapNonNull(typ)
|
|
||||||
|
|
||||||
// a reflect.Value of a nil interface will show up as an Invalid value
|
|
||||||
if resolver.Kind() == reflect.Invalid || ((resolver.Kind() == reflect.Ptr || resolver.Kind() == reflect.Interface) && resolver.IsNil()) {
|
|
||||||
// If a field of a non-null type resolves to null (either because the
|
|
||||||
// function to resolve the field returned null or because an error occurred),
|
|
||||||
// add an error to the "errors" list in the response.
|
|
||||||
if nonNull {
|
|
||||||
err := errors.Errorf("graphql: got nil for non-null %q", t)
|
|
||||||
err.Path = path.toSlice()
|
|
||||||
r.AddError(err)
|
|
||||||
}
|
|
||||||
out.WriteString("null")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch t.(type) {
|
|
||||||
case *types.ObjectTypeDefinition, *types.InterfaceTypeDefinition, *types.Union:
|
|
||||||
r.execSelections(ctx, sels, path, s, resolver, out, false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Any pointers or interfaces at this point should be non-nil, so we can get the actual value of them
|
|
||||||
// for serialization
|
|
||||||
if resolver.Kind() == reflect.Ptr || resolver.Kind() == reflect.Interface {
|
|
||||||
resolver = resolver.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
switch t := t.(type) {
|
|
||||||
case *types.List:
|
|
||||||
r.execList(ctx, sels, t, path, s, resolver, out)
|
|
||||||
|
|
||||||
case *types.ScalarTypeDefinition:
|
|
||||||
v := resolver.Interface()
|
|
||||||
data, err := json.Marshal(v)
|
|
||||||
if err != nil {
|
|
||||||
panic(errors.Errorf("could not marshal %v: %s", v, err))
|
|
||||||
}
|
|
||||||
out.Write(data)
|
|
||||||
|
|
||||||
case *types.EnumTypeDefinition:
|
|
||||||
var stringer fmt.Stringer = resolver
|
|
||||||
if s, ok := resolver.Interface().(fmt.Stringer); ok {
|
|
||||||
stringer = s
|
|
||||||
}
|
|
||||||
name := stringer.String()
|
|
||||||
var valid bool
|
|
||||||
for _, v := range t.EnumValuesDefinition {
|
|
||||||
if v.EnumValue == name {
|
|
||||||
valid = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !valid {
|
|
||||||
err := errors.Errorf("Invalid value %s.\nExpected type %s, found %s.", name, t.Name, name)
|
|
||||||
err.Path = path.toSlice()
|
|
||||||
r.AddError(err)
|
|
||||||
out.WriteString("null")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
out.WriteByte('"')
|
|
||||||
out.WriteString(name)
|
|
||||||
out.WriteByte('"')
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Request) execList(ctx context.Context, sels []selected.Selection, typ *types.List, path *pathSegment, s *resolvable.Schema, resolver reflect.Value, out *bytes.Buffer) {
|
|
||||||
l := resolver.Len()
|
|
||||||
entryouts := make([]bytes.Buffer, l)
|
|
||||||
|
|
||||||
if selected.HasAsyncSel(sels) {
|
|
||||||
// Limit the number of concurrent goroutines spawned as it can lead to large
|
|
||||||
// memory spikes for large lists.
|
|
||||||
concurrency := cap(r.Limiter)
|
|
||||||
sem := make(chan struct{}, concurrency)
|
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
sem <- struct{}{}
|
|
||||||
go func(i int) {
|
|
||||||
defer func() { <-sem }()
|
|
||||||
defer r.handlePanic(ctx)
|
|
||||||
r.execSelectionSet(ctx, sels, typ.OfType, &pathSegment{path, i}, s, resolver.Index(i), &entryouts[i])
|
|
||||||
}(i)
|
|
||||||
}
|
|
||||||
for i := 0; i < concurrency; i++ {
|
|
||||||
sem <- struct{}{}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
r.execSelectionSet(ctx, sels, typ.OfType, &pathSegment{path, i}, s, resolver.Index(i), &entryouts[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_, listOfNonNull := typ.OfType.(*types.NonNull)
|
|
||||||
|
|
||||||
out.WriteByte('[')
|
|
||||||
for i, entryout := range entryouts {
|
|
||||||
// If the list wraps a non-null type and one of the list elements
|
|
||||||
// resolves to null, then the entire list resolves to null.
|
|
||||||
if listOfNonNull && resolvedToNull(&entryout) {
|
|
||||||
out.Reset()
|
|
||||||
out.WriteString("null")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if i > 0 {
|
|
||||||
out.WriteByte(',')
|
|
||||||
}
|
|
||||||
out.Write(entryout.Bytes())
|
|
||||||
}
|
|
||||||
out.WriteByte(']')
|
|
||||||
}
|
|
||||||
|
|
||||||
func unwrapNonNull(t types.Type) (types.Type, bool) {
|
|
||||||
if nn, ok := t.(*types.NonNull); ok {
|
|
||||||
return nn.OfType, true
|
|
||||||
}
|
|
||||||
return t, false
|
|
||||||
}
|
|
||||||
|
|
||||||
type pathSegment struct {
|
|
||||||
parent *pathSegment
|
|
||||||
value interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pathSegment) toSlice() []interface{} {
|
|
||||||
if p == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return append(p.parent.toSlice(), p.value)
|
|
||||||
}
|
|
390
vendor/github.com/graph-gophers/graphql-go/internal/exec/packer/packer.go
generated
vendored
390
vendor/github.com/graph-gophers/graphql-go/internal/exec/packer/packer.go
generated
vendored
@ -1,390 +0,0 @@
|
|||||||
package packer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/graph-gophers/graphql-go/decode"
|
|
||||||
"github.com/graph-gophers/graphql-go/errors"
|
|
||||||
"github.com/graph-gophers/graphql-go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type packer interface {
|
|
||||||
Pack(value interface{}) (reflect.Value, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Builder struct {
|
|
||||||
packerMap map[typePair]*packerMapEntry
|
|
||||||
structPackers []*StructPacker
|
|
||||||
}
|
|
||||||
|
|
||||||
type typePair struct {
|
|
||||||
graphQLType types.Type
|
|
||||||
resolverType reflect.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
type packerMapEntry struct {
|
|
||||||
packer packer
|
|
||||||
targets []*packer
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBuilder() *Builder {
|
|
||||||
return &Builder{
|
|
||||||
packerMap: make(map[typePair]*packerMapEntry),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) Finish() error {
|
|
||||||
for _, entry := range b.packerMap {
|
|
||||||
for _, target := range entry.targets {
|
|
||||||
*target = entry.packer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range b.structPackers {
|
|
||||||
p.defaultStruct = reflect.New(p.structType).Elem()
|
|
||||||
for _, f := range p.fields {
|
|
||||||
if defaultVal := f.field.Default; defaultVal != nil {
|
|
||||||
v, err := f.fieldPacker.Pack(defaultVal.Deserialize(nil))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
p.defaultStruct.FieldByIndex(f.fieldIndex).Set(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) assignPacker(target *packer, schemaType types.Type, reflectType reflect.Type) error {
|
|
||||||
k := typePair{schemaType, reflectType}
|
|
||||||
ref, ok := b.packerMap[k]
|
|
||||||
if !ok {
|
|
||||||
ref = &packerMapEntry{}
|
|
||||||
b.packerMap[k] = ref
|
|
||||||
var err error
|
|
||||||
ref.packer, err = b.makePacker(schemaType, reflectType)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ref.targets = append(ref.targets, target)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) makePacker(schemaType types.Type, reflectType reflect.Type) (packer, error) {
|
|
||||||
t, nonNull := unwrapNonNull(schemaType)
|
|
||||||
if !nonNull {
|
|
||||||
if reflectType.Kind() == reflect.Ptr {
|
|
||||||
elemType := reflectType.Elem()
|
|
||||||
addPtr := true
|
|
||||||
if _, ok := t.(*types.InputObject); ok {
|
|
||||||
elemType = reflectType // keep pointer for input objects
|
|
||||||
addPtr = false
|
|
||||||
}
|
|
||||||
elem, err := b.makeNonNullPacker(t, elemType)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &nullPacker{
|
|
||||||
elemPacker: elem,
|
|
||||||
valueType: reflectType,
|
|
||||||
addPtr: addPtr,
|
|
||||||
}, nil
|
|
||||||
} else if isNullable(reflectType) {
|
|
||||||
elemType := reflectType
|
|
||||||
addPtr := false
|
|
||||||
elem, err := b.makeNonNullPacker(t, elemType)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &nullPacker{
|
|
||||||
elemPacker: elem,
|
|
||||||
valueType: reflectType,
|
|
||||||
addPtr: addPtr,
|
|
||||||
}, nil
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("%s is not a pointer or a nullable type", reflectType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.makeNonNullPacker(t, reflectType)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) makeNonNullPacker(schemaType types.Type, reflectType reflect.Type) (packer, error) {
|
|
||||||
if u, ok := reflect.New(reflectType).Interface().(decode.Unmarshaler); ok {
|
|
||||||
if !u.ImplementsGraphQLType(schemaType.String()) {
|
|
||||||
return nil, fmt.Errorf("can not unmarshal %s into %s", schemaType, reflectType)
|
|
||||||
}
|
|
||||||
return &unmarshalerPacker{
|
|
||||||
ValueType: reflectType,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch t := schemaType.(type) {
|
|
||||||
case *types.ScalarTypeDefinition:
|
|
||||||
return &ValuePacker{
|
|
||||||
ValueType: reflectType,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
case *types.EnumTypeDefinition:
|
|
||||||
if reflectType.Kind() != reflect.String {
|
|
||||||
return nil, fmt.Errorf("wrong type, expected %s", reflect.String)
|
|
||||||
}
|
|
||||||
return &ValuePacker{
|
|
||||||
ValueType: reflectType,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
case *types.InputObject:
|
|
||||||
e, err := b.MakeStructPacker(t.Values, reflectType)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return e, nil
|
|
||||||
|
|
||||||
case *types.List:
|
|
||||||
if reflectType.Kind() != reflect.Slice {
|
|
||||||
return nil, fmt.Errorf("expected slice, got %s", reflectType)
|
|
||||||
}
|
|
||||||
p := &listPacker{
|
|
||||||
sliceType: reflectType,
|
|
||||||
}
|
|
||||||
if err := b.assignPacker(&p.elem, t.OfType, reflectType.Elem()); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return p, nil
|
|
||||||
|
|
||||||
case *types.ObjectTypeDefinition, *types.InterfaceTypeDefinition, *types.Union:
|
|
||||||
return nil, fmt.Errorf("type of kind %s can not be used as input", t.Kind())
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) MakeStructPacker(values []*types.InputValueDefinition, typ reflect.Type) (*StructPacker, error) {
|
|
||||||
structType := typ
|
|
||||||
usePtr := false
|
|
||||||
if typ.Kind() == reflect.Ptr {
|
|
||||||
structType = typ.Elem()
|
|
||||||
usePtr = true
|
|
||||||
}
|
|
||||||
if structType.Kind() != reflect.Struct {
|
|
||||||
return nil, fmt.Errorf("expected struct or pointer to struct, got %s (hint: missing `args struct { ... }` wrapper for field arguments?)", typ)
|
|
||||||
}
|
|
||||||
|
|
||||||
var fields []*structPackerField
|
|
||||||
for _, v := range values {
|
|
||||||
fe := &structPackerField{field: v}
|
|
||||||
fx := func(n string) bool {
|
|
||||||
return strings.EqualFold(stripUnderscore(n), stripUnderscore(v.Name.Name))
|
|
||||||
}
|
|
||||||
|
|
||||||
sf, ok := structType.FieldByNameFunc(fx)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("%s does not define field %q (hint: missing `args struct { ... }` wrapper for field arguments, or missing field on input struct)", typ, v.Name.Name)
|
|
||||||
}
|
|
||||||
if sf.PkgPath != "" {
|
|
||||||
return nil, fmt.Errorf("field %q must be exported", sf.Name)
|
|
||||||
}
|
|
||||||
fe.fieldIndex = sf.Index
|
|
||||||
|
|
||||||
ft := v.Type
|
|
||||||
if v.Default != nil {
|
|
||||||
ft, _ = unwrapNonNull(ft)
|
|
||||||
ft = &types.NonNull{OfType: ft}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := b.assignPacker(&fe.fieldPacker, ft, sf.Type); err != nil {
|
|
||||||
return nil, fmt.Errorf("field %q: %s", sf.Name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fields = append(fields, fe)
|
|
||||||
}
|
|
||||||
|
|
||||||
p := &StructPacker{
|
|
||||||
structType: structType,
|
|
||||||
usePtr: usePtr,
|
|
||||||
fields: fields,
|
|
||||||
}
|
|
||||||
b.structPackers = append(b.structPackers, p)
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type StructPacker struct {
|
|
||||||
structType reflect.Type
|
|
||||||
usePtr bool
|
|
||||||
defaultStruct reflect.Value
|
|
||||||
fields []*structPackerField
|
|
||||||
}
|
|
||||||
|
|
||||||
type structPackerField struct {
|
|
||||||
field *types.InputValueDefinition
|
|
||||||
fieldIndex []int
|
|
||||||
fieldPacker packer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *StructPacker) Pack(value interface{}) (reflect.Value, error) {
|
|
||||||
if value == nil {
|
|
||||||
return reflect.Value{}, errors.Errorf("got null for non-null")
|
|
||||||
}
|
|
||||||
|
|
||||||
values := value.(map[string]interface{})
|
|
||||||
v := reflect.New(p.structType)
|
|
||||||
v.Elem().Set(p.defaultStruct)
|
|
||||||
for _, f := range p.fields {
|
|
||||||
if value, ok := values[f.field.Name.Name]; ok {
|
|
||||||
packed, err := f.fieldPacker.Pack(value)
|
|
||||||
if err != nil {
|
|
||||||
return reflect.Value{}, err
|
|
||||||
}
|
|
||||||
v.Elem().FieldByIndex(f.fieldIndex).Set(packed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !p.usePtr {
|
|
||||||
return v.Elem(), nil
|
|
||||||
}
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type listPacker struct {
|
|
||||||
sliceType reflect.Type
|
|
||||||
elem packer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *listPacker) Pack(value interface{}) (reflect.Value, error) {
|
|
||||||
list, ok := value.([]interface{})
|
|
||||||
if !ok {
|
|
||||||
list = []interface{}{value}
|
|
||||||
}
|
|
||||||
|
|
||||||
v := reflect.MakeSlice(e.sliceType, len(list), len(list))
|
|
||||||
for i := range list {
|
|
||||||
packed, err := e.elem.Pack(list[i])
|
|
||||||
if err != nil {
|
|
||||||
return reflect.Value{}, err
|
|
||||||
}
|
|
||||||
v.Index(i).Set(packed)
|
|
||||||
}
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type nullPacker struct {
|
|
||||||
elemPacker packer
|
|
||||||
valueType reflect.Type
|
|
||||||
addPtr bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *nullPacker) Pack(value interface{}) (reflect.Value, error) {
|
|
||||||
if value == nil && !isNullable(p.valueType) {
|
|
||||||
return reflect.Zero(p.valueType), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
v, err := p.elemPacker.Pack(value)
|
|
||||||
if err != nil {
|
|
||||||
return reflect.Value{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.addPtr {
|
|
||||||
ptr := reflect.New(p.valueType.Elem())
|
|
||||||
ptr.Elem().Set(v)
|
|
||||||
return ptr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ValuePacker struct {
|
|
||||||
ValueType reflect.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ValuePacker) Pack(value interface{}) (reflect.Value, error) {
|
|
||||||
if value == nil {
|
|
||||||
return reflect.Value{}, errors.Errorf("got null for non-null")
|
|
||||||
}
|
|
||||||
|
|
||||||
coerced, err := unmarshalInput(p.ValueType, value)
|
|
||||||
if err != nil {
|
|
||||||
return reflect.Value{}, fmt.Errorf("could not unmarshal %#v (%T) into %s: %s", value, value, p.ValueType, err)
|
|
||||||
}
|
|
||||||
return reflect.ValueOf(coerced), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type unmarshalerPacker struct {
|
|
||||||
ValueType reflect.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *unmarshalerPacker) Pack(value interface{}) (reflect.Value, error) {
|
|
||||||
if value == nil && !isNullable(p.ValueType) {
|
|
||||||
return reflect.Value{}, errors.Errorf("got null for non-null")
|
|
||||||
}
|
|
||||||
|
|
||||||
v := reflect.New(p.ValueType)
|
|
||||||
if err := v.Interface().(decode.Unmarshaler).UnmarshalGraphQL(value); err != nil {
|
|
||||||
return reflect.Value{}, err
|
|
||||||
}
|
|
||||||
return v.Elem(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmarshalInput(typ reflect.Type, input interface{}) (interface{}, error) {
|
|
||||||
if reflect.TypeOf(input) == typ {
|
|
||||||
return input, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch typ.Kind() {
|
|
||||||
case reflect.Int32:
|
|
||||||
switch input := input.(type) {
|
|
||||||
case int:
|
|
||||||
if input < math.MinInt32 || input > math.MaxInt32 {
|
|
||||||
return nil, fmt.Errorf("not a 32-bit integer")
|
|
||||||
}
|
|
||||||
return int32(input), nil
|
|
||||||
case float64:
|
|
||||||
coerced := int32(input)
|
|
||||||
if input < math.MinInt32 || input > math.MaxInt32 || float64(coerced) != input {
|
|
||||||
return nil, fmt.Errorf("not a 32-bit integer")
|
|
||||||
}
|
|
||||||
return coerced, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Float64:
|
|
||||||
switch input := input.(type) {
|
|
||||||
case int32:
|
|
||||||
return float64(input), nil
|
|
||||||
case int:
|
|
||||||
return float64(input), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.String:
|
|
||||||
if reflect.TypeOf(input).ConvertibleTo(typ) {
|
|
||||||
return reflect.ValueOf(input).Convert(typ).Interface(), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("incompatible type")
|
|
||||||
}
|
|
||||||
|
|
||||||
func unwrapNonNull(t types.Type) (types.Type, bool) {
|
|
||||||
if nn, ok := t.(*types.NonNull); ok {
|
|
||||||
return nn.OfType, true
|
|
||||||
}
|
|
||||||
return t, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func stripUnderscore(s string) string {
|
|
||||||
return strings.Replace(s, "_", "", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NullUnmarshaller is an unmarshaller that can handle a nil input
|
|
||||||
type NullUnmarshaller interface {
|
|
||||||
decode.Unmarshaler
|
|
||||||
Nullable()
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNullable(t reflect.Type) bool {
|
|
||||||
_, ok := reflect.New(t).Interface().(NullUnmarshaller)
|
|
||||||
return ok
|
|
||||||
}
|
|
70
vendor/github.com/graph-gophers/graphql-go/internal/exec/resolvable/meta.go
generated
vendored
70
vendor/github.com/graph-gophers/graphql-go/internal/exec/resolvable/meta.go
generated
vendored
@ -1,70 +0,0 @@
|
|||||||
package resolvable
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/graph-gophers/graphql-go/introspection"
|
|
||||||
"github.com/graph-gophers/graphql-go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Meta defines the details of the metadata schema for introspection.
|
|
||||||
type Meta struct {
|
|
||||||
FieldSchema Field
|
|
||||||
FieldType Field
|
|
||||||
FieldTypename Field
|
|
||||||
Schema *Object
|
|
||||||
Type *Object
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMeta(s *types.Schema) *Meta {
|
|
||||||
var err error
|
|
||||||
b := newBuilder(s)
|
|
||||||
|
|
||||||
metaSchema := s.Types["__Schema"].(*types.ObjectTypeDefinition)
|
|
||||||
so, err := b.makeObjectExec(metaSchema.Name, metaSchema.Fields, nil, false, reflect.TypeOf(&introspection.Schema{}))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
metaType := s.Types["__Type"].(*types.ObjectTypeDefinition)
|
|
||||||
t, err := b.makeObjectExec(metaType.Name, metaType.Fields, nil, false, reflect.TypeOf(&introspection.Type{}))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := b.finish(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldTypename := Field{
|
|
||||||
FieldDefinition: types.FieldDefinition{
|
|
||||||
Name: "__typename",
|
|
||||||
Type: &types.NonNull{OfType: s.Types["String"]},
|
|
||||||
},
|
|
||||||
TraceLabel: "GraphQL field: __typename",
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldSchema := Field{
|
|
||||||
FieldDefinition: types.FieldDefinition{
|
|
||||||
Name: "__schema",
|
|
||||||
Type: s.Types["__Schema"],
|
|
||||||
},
|
|
||||||
TraceLabel: "GraphQL field: __schema",
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldType := Field{
|
|
||||||
FieldDefinition: types.FieldDefinition{
|
|
||||||
Name: "__type",
|
|
||||||
Type: s.Types["__Type"],
|
|
||||||
},
|
|
||||||
TraceLabel: "GraphQL field: __type",
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Meta{
|
|
||||||
FieldSchema: fieldSchema,
|
|
||||||
FieldTypename: fieldTypename,
|
|
||||||
FieldType: fieldType,
|
|
||||||
Schema: so,
|
|
||||||
Type: t,
|
|
||||||
}
|
|
||||||
}
|
|
453
vendor/github.com/graph-gophers/graphql-go/internal/exec/resolvable/resolvable.go
generated
vendored
453
vendor/github.com/graph-gophers/graphql-go/internal/exec/resolvable/resolvable.go
generated
vendored
@ -1,453 +0,0 @@
|
|||||||
package resolvable
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/graph-gophers/graphql-go/decode"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/exec/packer"
|
|
||||||
"github.com/graph-gophers/graphql-go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Schema struct {
|
|
||||||
*Meta
|
|
||||||
types.Schema
|
|
||||||
Query Resolvable
|
|
||||||
Mutation Resolvable
|
|
||||||
Subscription Resolvable
|
|
||||||
Resolver reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
type Resolvable interface {
|
|
||||||
isResolvable()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Object struct {
|
|
||||||
Name string
|
|
||||||
Fields map[string]*Field
|
|
||||||
TypeAssertions map[string]*TypeAssertion
|
|
||||||
}
|
|
||||||
|
|
||||||
type Field struct {
|
|
||||||
types.FieldDefinition
|
|
||||||
TypeName string
|
|
||||||
MethodIndex int
|
|
||||||
FieldIndex []int
|
|
||||||
HasContext bool
|
|
||||||
HasError bool
|
|
||||||
ArgsPacker *packer.StructPacker
|
|
||||||
ValueExec Resolvable
|
|
||||||
TraceLabel string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Field) UseMethodResolver() bool {
|
|
||||||
return len(f.FieldIndex) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type TypeAssertion struct {
|
|
||||||
MethodIndex int
|
|
||||||
TypeExec Resolvable
|
|
||||||
}
|
|
||||||
|
|
||||||
type List struct {
|
|
||||||
Elem Resolvable
|
|
||||||
}
|
|
||||||
|
|
||||||
type Scalar struct{}
|
|
||||||
|
|
||||||
func (*Object) isResolvable() {}
|
|
||||||
func (*List) isResolvable() {}
|
|
||||||
func (*Scalar) isResolvable() {}
|
|
||||||
|
|
||||||
func ApplyResolver(s *types.Schema, resolver interface{}) (*Schema, error) {
|
|
||||||
if resolver == nil {
|
|
||||||
return &Schema{Meta: newMeta(s), Schema: *s}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
b := newBuilder(s)
|
|
||||||
|
|
||||||
var query, mutation, subscription Resolvable
|
|
||||||
|
|
||||||
if t, ok := s.EntryPoints["query"]; ok {
|
|
||||||
if err := b.assignExec(&query, t, reflect.TypeOf(resolver)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if t, ok := s.EntryPoints["mutation"]; ok {
|
|
||||||
if err := b.assignExec(&mutation, t, reflect.TypeOf(resolver)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if t, ok := s.EntryPoints["subscription"]; ok {
|
|
||||||
if err := b.assignExec(&subscription, t, reflect.TypeOf(resolver)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := b.finish(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Schema{
|
|
||||||
Meta: newMeta(s),
|
|
||||||
Schema: *s,
|
|
||||||
Resolver: reflect.ValueOf(resolver),
|
|
||||||
Query: query,
|
|
||||||
Mutation: mutation,
|
|
||||||
Subscription: subscription,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type execBuilder struct {
|
|
||||||
schema *types.Schema
|
|
||||||
resMap map[typePair]*resMapEntry
|
|
||||||
packerBuilder *packer.Builder
|
|
||||||
}
|
|
||||||
|
|
||||||
type typePair struct {
|
|
||||||
graphQLType types.Type
|
|
||||||
resolverType reflect.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
type resMapEntry struct {
|
|
||||||
exec Resolvable
|
|
||||||
targets []*Resolvable
|
|
||||||
}
|
|
||||||
|
|
||||||
func newBuilder(s *types.Schema) *execBuilder {
|
|
||||||
return &execBuilder{
|
|
||||||
schema: s,
|
|
||||||
resMap: make(map[typePair]*resMapEntry),
|
|
||||||
packerBuilder: packer.NewBuilder(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *execBuilder) finish() error {
|
|
||||||
for _, entry := range b.resMap {
|
|
||||||
for _, target := range entry.targets {
|
|
||||||
*target = entry.exec
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.packerBuilder.Finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *execBuilder) assignExec(target *Resolvable, t types.Type, resolverType reflect.Type) error {
|
|
||||||
k := typePair{t, resolverType}
|
|
||||||
ref, ok := b.resMap[k]
|
|
||||||
if !ok {
|
|
||||||
ref = &resMapEntry{}
|
|
||||||
b.resMap[k] = ref
|
|
||||||
var err error
|
|
||||||
ref.exec, err = b.makeExec(t, resolverType)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ref.targets = append(ref.targets, target)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *execBuilder) makeExec(t types.Type, resolverType reflect.Type) (Resolvable, error) {
|
|
||||||
var nonNull bool
|
|
||||||
t, nonNull = unwrapNonNull(t)
|
|
||||||
|
|
||||||
switch t := t.(type) {
|
|
||||||
case *types.ObjectTypeDefinition:
|
|
||||||
return b.makeObjectExec(t.Name, t.Fields, nil, nonNull, resolverType)
|
|
||||||
|
|
||||||
case *types.InterfaceTypeDefinition:
|
|
||||||
return b.makeObjectExec(t.Name, t.Fields, t.PossibleTypes, nonNull, resolverType)
|
|
||||||
|
|
||||||
case *types.Union:
|
|
||||||
return b.makeObjectExec(t.Name, nil, t.UnionMemberTypes, nonNull, resolverType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !nonNull {
|
|
||||||
if resolverType.Kind() != reflect.Ptr {
|
|
||||||
return nil, fmt.Errorf("%s is not a pointer", resolverType)
|
|
||||||
}
|
|
||||||
resolverType = resolverType.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
switch t := t.(type) {
|
|
||||||
case *types.ScalarTypeDefinition:
|
|
||||||
return makeScalarExec(t, resolverType)
|
|
||||||
|
|
||||||
case *types.EnumTypeDefinition:
|
|
||||||
return &Scalar{}, nil
|
|
||||||
|
|
||||||
case *types.List:
|
|
||||||
if resolverType.Kind() != reflect.Slice {
|
|
||||||
return nil, fmt.Errorf("%s is not a slice", resolverType)
|
|
||||||
}
|
|
||||||
e := &List{}
|
|
||||||
if err := b.assignExec(&e.Elem, t.OfType, resolverType.Elem()); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return e, nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic("invalid type: " + t.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeScalarExec(t *types.ScalarTypeDefinition, resolverType reflect.Type) (Resolvable, error) {
|
|
||||||
implementsType := false
|
|
||||||
switch r := reflect.New(resolverType).Interface().(type) {
|
|
||||||
case *int32:
|
|
||||||
implementsType = t.Name == "Int"
|
|
||||||
case *float64:
|
|
||||||
implementsType = t.Name == "Float"
|
|
||||||
case *string:
|
|
||||||
implementsType = t.Name == "String"
|
|
||||||
case *bool:
|
|
||||||
implementsType = t.Name == "Boolean"
|
|
||||||
case decode.Unmarshaler:
|
|
||||||
implementsType = r.ImplementsGraphQLType(t.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !implementsType {
|
|
||||||
return nil, fmt.Errorf("can not use %s as %s", resolverType, t.Name)
|
|
||||||
}
|
|
||||||
return &Scalar{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *execBuilder) makeObjectExec(typeName string, fields types.FieldsDefinition, possibleTypes []*types.ObjectTypeDefinition,
|
|
||||||
nonNull bool, resolverType reflect.Type) (*Object, error) {
|
|
||||||
if !nonNull {
|
|
||||||
if resolverType.Kind() != reflect.Ptr && resolverType.Kind() != reflect.Interface {
|
|
||||||
return nil, fmt.Errorf("%s is not a pointer or interface", resolverType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
methodHasReceiver := resolverType.Kind() != reflect.Interface
|
|
||||||
|
|
||||||
Fields := make(map[string]*Field)
|
|
||||||
rt := unwrapPtr(resolverType)
|
|
||||||
fieldsCount := fieldCount(rt, map[string]int{})
|
|
||||||
for _, f := range fields {
|
|
||||||
var fieldIndex []int
|
|
||||||
methodIndex := findMethod(resolverType, f.Name)
|
|
||||||
if b.schema.UseFieldResolvers && methodIndex == -1 {
|
|
||||||
if fieldsCount[strings.ToLower(stripUnderscore(f.Name))] > 1 {
|
|
||||||
return nil, fmt.Errorf("%s does not resolve %q: ambiguous field %q", resolverType, typeName, f.Name)
|
|
||||||
}
|
|
||||||
fieldIndex = findField(rt, f.Name, []int{})
|
|
||||||
}
|
|
||||||
if methodIndex == -1 && len(fieldIndex) == 0 {
|
|
||||||
hint := ""
|
|
||||||
if findMethod(reflect.PtrTo(resolverType), f.Name) != -1 {
|
|
||||||
hint = " (hint: the method exists on the pointer type)"
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("%s does not resolve %q: missing method for field %q%s", resolverType, typeName, f.Name, hint)
|
|
||||||
}
|
|
||||||
|
|
||||||
var m reflect.Method
|
|
||||||
var sf reflect.StructField
|
|
||||||
if methodIndex != -1 {
|
|
||||||
m = resolverType.Method(methodIndex)
|
|
||||||
} else {
|
|
||||||
sf = rt.FieldByIndex(fieldIndex)
|
|
||||||
}
|
|
||||||
fe, err := b.makeFieldExec(typeName, f, m, sf, methodIndex, fieldIndex, methodHasReceiver)
|
|
||||||
if err != nil {
|
|
||||||
var resolverName string
|
|
||||||
if methodIndex != -1 {
|
|
||||||
resolverName = m.Name
|
|
||||||
} else {
|
|
||||||
resolverName = sf.Name
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("%s\n\tused by (%s).%s", err, resolverType, resolverName)
|
|
||||||
}
|
|
||||||
Fields[f.Name] = fe
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check type assertions when
|
|
||||||
// 1) using method resolvers
|
|
||||||
// 2) Or resolver is not an interface type
|
|
||||||
typeAssertions := make(map[string]*TypeAssertion)
|
|
||||||
if !b.schema.UseFieldResolvers || resolverType.Kind() != reflect.Interface {
|
|
||||||
for _, impl := range possibleTypes {
|
|
||||||
methodIndex := findMethod(resolverType, "To"+impl.Name)
|
|
||||||
if methodIndex == -1 {
|
|
||||||
return nil, fmt.Errorf("%s does not resolve %q: missing method %q to convert to %q", resolverType, typeName, "To"+impl.Name, impl.Name)
|
|
||||||
}
|
|
||||||
if resolverType.Method(methodIndex).Type.NumOut() != 2 {
|
|
||||||
return nil, fmt.Errorf("%s does not resolve %q: method %q should return a value and a bool indicating success", resolverType, typeName, "To"+impl.Name)
|
|
||||||
}
|
|
||||||
a := &TypeAssertion{
|
|
||||||
MethodIndex: methodIndex,
|
|
||||||
}
|
|
||||||
if err := b.assignExec(&a.TypeExec, impl, resolverType.Method(methodIndex).Type.Out(0)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
typeAssertions[impl.Name] = a
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Object{
|
|
||||||
Name: typeName,
|
|
||||||
Fields: Fields,
|
|
||||||
TypeAssertions: typeAssertions,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var contextType = reflect.TypeOf((*context.Context)(nil)).Elem()
|
|
||||||
var errorType = reflect.TypeOf((*error)(nil)).Elem()
|
|
||||||
|
|
||||||
func (b *execBuilder) makeFieldExec(typeName string, f *types.FieldDefinition, m reflect.Method, sf reflect.StructField,
|
|
||||||
methodIndex int, fieldIndex []int, methodHasReceiver bool) (*Field, error) {
|
|
||||||
|
|
||||||
var argsPacker *packer.StructPacker
|
|
||||||
var hasError bool
|
|
||||||
var hasContext bool
|
|
||||||
|
|
||||||
// Validate resolver method only when there is one
|
|
||||||
if methodIndex != -1 {
|
|
||||||
in := make([]reflect.Type, m.Type.NumIn())
|
|
||||||
for i := range in {
|
|
||||||
in[i] = m.Type.In(i)
|
|
||||||
}
|
|
||||||
if methodHasReceiver {
|
|
||||||
in = in[1:] // first parameter is receiver
|
|
||||||
}
|
|
||||||
|
|
||||||
hasContext = len(in) > 0 && in[0] == contextType
|
|
||||||
if hasContext {
|
|
||||||
in = in[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(f.Arguments) > 0 {
|
|
||||||
if len(in) == 0 {
|
|
||||||
return nil, fmt.Errorf("must have parameter for field arguments")
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
argsPacker, err = b.packerBuilder.MakeStructPacker(f.Arguments, in[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
in = in[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(in) > 0 {
|
|
||||||
return nil, fmt.Errorf("too many parameters")
|
|
||||||
}
|
|
||||||
|
|
||||||
maxNumOfReturns := 2
|
|
||||||
if m.Type.NumOut() < maxNumOfReturns-1 {
|
|
||||||
return nil, fmt.Errorf("too few return values")
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.Type.NumOut() > maxNumOfReturns {
|
|
||||||
return nil, fmt.Errorf("too many return values")
|
|
||||||
}
|
|
||||||
|
|
||||||
hasError = m.Type.NumOut() == maxNumOfReturns
|
|
||||||
if hasError {
|
|
||||||
if m.Type.Out(maxNumOfReturns-1) != errorType {
|
|
||||||
return nil, fmt.Errorf(`must have "error" as its last return value`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fe := &Field{
|
|
||||||
FieldDefinition: *f,
|
|
||||||
TypeName: typeName,
|
|
||||||
MethodIndex: methodIndex,
|
|
||||||
FieldIndex: fieldIndex,
|
|
||||||
HasContext: hasContext,
|
|
||||||
ArgsPacker: argsPacker,
|
|
||||||
HasError: hasError,
|
|
||||||
TraceLabel: fmt.Sprintf("GraphQL field: %s.%s", typeName, f.Name),
|
|
||||||
}
|
|
||||||
|
|
||||||
var out reflect.Type
|
|
||||||
if methodIndex != -1 {
|
|
||||||
out = m.Type.Out(0)
|
|
||||||
sub, ok := b.schema.EntryPoints["subscription"]
|
|
||||||
if ok && typeName == sub.TypeName() && out.Kind() == reflect.Chan {
|
|
||||||
out = m.Type.Out(0).Elem()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out = sf.Type
|
|
||||||
}
|
|
||||||
if err := b.assignExec(&fe.ValueExec, f.Type, out); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return fe, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func findMethod(t reflect.Type, name string) int {
|
|
||||||
for i := 0; i < t.NumMethod(); i++ {
|
|
||||||
if strings.EqualFold(stripUnderscore(name), stripUnderscore(t.Method(i).Name)) {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func findField(t reflect.Type, name string, index []int) []int {
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
field := t.Field(i)
|
|
||||||
|
|
||||||
if field.Type.Kind() == reflect.Struct && field.Anonymous {
|
|
||||||
newIndex := findField(field.Type, name, []int{i})
|
|
||||||
if len(newIndex) > 1 {
|
|
||||||
return append(index, newIndex...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.EqualFold(stripUnderscore(name), stripUnderscore(field.Name)) {
|
|
||||||
return append(index, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return index
|
|
||||||
}
|
|
||||||
|
|
||||||
// fieldCount helps resolve ambiguity when more than one embedded struct contains fields with the same name.
|
|
||||||
func fieldCount(t reflect.Type, count map[string]int) map[string]int {
|
|
||||||
if t.Kind() != reflect.Struct {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
field := t.Field(i)
|
|
||||||
fieldName := strings.ToLower(stripUnderscore(field.Name))
|
|
||||||
|
|
||||||
if field.Type.Kind() == reflect.Struct && field.Anonymous {
|
|
||||||
count = fieldCount(field.Type, count)
|
|
||||||
} else {
|
|
||||||
if _, ok := count[fieldName]; !ok {
|
|
||||||
count[fieldName] = 0
|
|
||||||
}
|
|
||||||
count[fieldName]++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
func unwrapNonNull(t types.Type) (types.Type, bool) {
|
|
||||||
if nn, ok := t.(*types.NonNull); ok {
|
|
||||||
return nn.OfType, true
|
|
||||||
}
|
|
||||||
return t, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func stripUnderscore(s string) string {
|
|
||||||
return strings.Replace(s, "_", "", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func unwrapPtr(t reflect.Type) reflect.Type {
|
|
||||||
if t.Kind() == reflect.Ptr {
|
|
||||||
return t.Elem()
|
|
||||||
}
|
|
||||||
return t
|
|
||||||
}
|
|
269
vendor/github.com/graph-gophers/graphql-go/internal/exec/selected/selected.go
generated
vendored
269
vendor/github.com/graph-gophers/graphql-go/internal/exec/selected/selected.go
generated
vendored
@ -1,269 +0,0 @@
|
|||||||
package selected
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/graph-gophers/graphql-go/errors"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/exec/packer"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/exec/resolvable"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/query"
|
|
||||||
"github.com/graph-gophers/graphql-go/introspection"
|
|
||||||
"github.com/graph-gophers/graphql-go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Request struct {
|
|
||||||
Schema *types.Schema
|
|
||||||
Doc *types.ExecutableDefinition
|
|
||||||
Vars map[string]interface{}
|
|
||||||
Mu sync.Mutex
|
|
||||||
Errs []*errors.QueryError
|
|
||||||
DisableIntrospection bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Request) AddError(err *errors.QueryError) {
|
|
||||||
r.Mu.Lock()
|
|
||||||
r.Errs = append(r.Errs, err)
|
|
||||||
r.Mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func ApplyOperation(r *Request, s *resolvable.Schema, op *types.OperationDefinition) []Selection {
|
|
||||||
var obj *resolvable.Object
|
|
||||||
switch op.Type {
|
|
||||||
case query.Query:
|
|
||||||
obj = s.Query.(*resolvable.Object)
|
|
||||||
case query.Mutation:
|
|
||||||
obj = s.Mutation.(*resolvable.Object)
|
|
||||||
case query.Subscription:
|
|
||||||
obj = s.Subscription.(*resolvable.Object)
|
|
||||||
}
|
|
||||||
return applySelectionSet(r, s, obj, op.Selections)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Selection interface {
|
|
||||||
isSelection()
|
|
||||||
}
|
|
||||||
|
|
||||||
type SchemaField struct {
|
|
||||||
resolvable.Field
|
|
||||||
Alias string
|
|
||||||
Args map[string]interface{}
|
|
||||||
PackedArgs reflect.Value
|
|
||||||
Sels []Selection
|
|
||||||
Async bool
|
|
||||||
FixedResult reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
type TypeAssertion struct {
|
|
||||||
resolvable.TypeAssertion
|
|
||||||
Sels []Selection
|
|
||||||
}
|
|
||||||
|
|
||||||
type TypenameField struct {
|
|
||||||
resolvable.Object
|
|
||||||
Alias string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*SchemaField) isSelection() {}
|
|
||||||
func (*TypeAssertion) isSelection() {}
|
|
||||||
func (*TypenameField) isSelection() {}
|
|
||||||
|
|
||||||
func applySelectionSet(r *Request, s *resolvable.Schema, e *resolvable.Object, sels []types.Selection) (flattenedSels []Selection) {
|
|
||||||
for _, sel := range sels {
|
|
||||||
switch sel := sel.(type) {
|
|
||||||
case *types.Field:
|
|
||||||
field := sel
|
|
||||||
if skipByDirective(r, field.Directives) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch field.Name.Name {
|
|
||||||
case "__typename":
|
|
||||||
// __typename is available even though r.DisableIntrospection == true
|
|
||||||
// because it is necessary when using union types and interfaces: https://graphql.org/learn/schema/#union-types
|
|
||||||
flattenedSels = append(flattenedSels, &TypenameField{
|
|
||||||
Object: *e,
|
|
||||||
Alias: field.Alias.Name,
|
|
||||||
})
|
|
||||||
|
|
||||||
case "__schema":
|
|
||||||
if !r.DisableIntrospection {
|
|
||||||
flattenedSels = append(flattenedSels, &SchemaField{
|
|
||||||
Field: s.Meta.FieldSchema,
|
|
||||||
Alias: field.Alias.Name,
|
|
||||||
Sels: applySelectionSet(r, s, s.Meta.Schema, field.SelectionSet),
|
|
||||||
Async: true,
|
|
||||||
FixedResult: reflect.ValueOf(introspection.WrapSchema(r.Schema)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
case "__type":
|
|
||||||
if !r.DisableIntrospection {
|
|
||||||
p := packer.ValuePacker{ValueType: reflect.TypeOf("")}
|
|
||||||
v, err := p.Pack(field.Arguments.MustGet("name").Deserialize(r.Vars))
|
|
||||||
if err != nil {
|
|
||||||
r.AddError(errors.Errorf("%s", err))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
t, ok := r.Schema.Types[v.String()]
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
flattenedSels = append(flattenedSels, &SchemaField{
|
|
||||||
Field: s.Meta.FieldType,
|
|
||||||
Alias: field.Alias.Name,
|
|
||||||
Sels: applySelectionSet(r, s, s.Meta.Type, field.SelectionSet),
|
|
||||||
Async: true,
|
|
||||||
FixedResult: reflect.ValueOf(introspection.WrapType(t)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
fe := e.Fields[field.Name.Name]
|
|
||||||
|
|
||||||
var args map[string]interface{}
|
|
||||||
var packedArgs reflect.Value
|
|
||||||
if fe.ArgsPacker != nil {
|
|
||||||
args = make(map[string]interface{})
|
|
||||||
for _, arg := range field.Arguments {
|
|
||||||
args[arg.Name.Name] = arg.Value.Deserialize(r.Vars)
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
packedArgs, err = fe.ArgsPacker.Pack(args)
|
|
||||||
if err != nil {
|
|
||||||
r.AddError(errors.Errorf("%s", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldSels := applyField(r, s, fe.ValueExec, field.SelectionSet)
|
|
||||||
flattenedSels = append(flattenedSels, &SchemaField{
|
|
||||||
Field: *fe,
|
|
||||||
Alias: field.Alias.Name,
|
|
||||||
Args: args,
|
|
||||||
PackedArgs: packedArgs,
|
|
||||||
Sels: fieldSels,
|
|
||||||
Async: fe.HasContext || fe.ArgsPacker != nil || fe.HasError || HasAsyncSel(fieldSels),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
case *types.InlineFragment:
|
|
||||||
frag := sel
|
|
||||||
if skipByDirective(r, frag.Directives) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
flattenedSels = append(flattenedSels, applyFragment(r, s, e, &frag.Fragment)...)
|
|
||||||
|
|
||||||
case *types.FragmentSpread:
|
|
||||||
spread := sel
|
|
||||||
if skipByDirective(r, spread.Directives) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
flattenedSels = append(flattenedSels, applyFragment(r, s, e, &r.Doc.Fragments.Get(spread.Name.Name).Fragment)...)
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic("invalid type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func applyFragment(r *Request, s *resolvable.Schema, e *resolvable.Object, frag *types.Fragment) []Selection {
|
|
||||||
if frag.On.Name != e.Name {
|
|
||||||
t := r.Schema.Resolve(frag.On.Name)
|
|
||||||
face, ok := t.(*types.InterfaceTypeDefinition)
|
|
||||||
if !ok && frag.On.Name != "" {
|
|
||||||
a, ok2 := e.TypeAssertions[frag.On.Name]
|
|
||||||
if !ok2 {
|
|
||||||
panic(fmt.Errorf("%q does not implement %q", frag.On, e.Name)) // TODO proper error handling
|
|
||||||
}
|
|
||||||
|
|
||||||
return []Selection{&TypeAssertion{
|
|
||||||
TypeAssertion: *a,
|
|
||||||
Sels: applySelectionSet(r, s, a.TypeExec.(*resolvable.Object), frag.Selections),
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
if ok && len(face.PossibleTypes) > 0 {
|
|
||||||
sels := []Selection{}
|
|
||||||
for _, t := range face.PossibleTypes {
|
|
||||||
if t.Name == e.Name {
|
|
||||||
return applySelectionSet(r, s, e, frag.Selections)
|
|
||||||
}
|
|
||||||
|
|
||||||
if a, ok := e.TypeAssertions[t.Name]; ok {
|
|
||||||
sels = append(sels, &TypeAssertion{
|
|
||||||
TypeAssertion: *a,
|
|
||||||
Sels: applySelectionSet(r, s, a.TypeExec.(*resolvable.Object), frag.Selections),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(sels) == 0 {
|
|
||||||
panic(fmt.Errorf("%q does not implement %q", e.Name, frag.On)) // TODO proper error handling
|
|
||||||
}
|
|
||||||
return sels
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return applySelectionSet(r, s, e, frag.Selections)
|
|
||||||
}
|
|
||||||
|
|
||||||
func applyField(r *Request, s *resolvable.Schema, e resolvable.Resolvable, sels []types.Selection) []Selection {
|
|
||||||
switch e := e.(type) {
|
|
||||||
case *resolvable.Object:
|
|
||||||
return applySelectionSet(r, s, e, sels)
|
|
||||||
case *resolvable.List:
|
|
||||||
return applyField(r, s, e.Elem, sels)
|
|
||||||
case *resolvable.Scalar:
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func skipByDirective(r *Request, directives types.DirectiveList) bool {
|
|
||||||
if d := directives.Get("skip"); d != nil {
|
|
||||||
p := packer.ValuePacker{ValueType: reflect.TypeOf(false)}
|
|
||||||
v, err := p.Pack(d.Arguments.MustGet("if").Deserialize(r.Vars))
|
|
||||||
if err != nil {
|
|
||||||
r.AddError(errors.Errorf("%s", err))
|
|
||||||
}
|
|
||||||
if err == nil && v.Bool() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if d := directives.Get("include"); d != nil {
|
|
||||||
p := packer.ValuePacker{ValueType: reflect.TypeOf(false)}
|
|
||||||
v, err := p.Pack(d.Arguments.MustGet("if").Deserialize(r.Vars))
|
|
||||||
if err != nil {
|
|
||||||
r.AddError(errors.Errorf("%s", err))
|
|
||||||
}
|
|
||||||
if err == nil && !v.Bool() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func HasAsyncSel(sels []Selection) bool {
|
|
||||||
for _, sel := range sels {
|
|
||||||
switch sel := sel.(type) {
|
|
||||||
case *SchemaField:
|
|
||||||
if sel.Async {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
case *TypeAssertion:
|
|
||||||
if HasAsyncSel(sel.Sels) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
case *TypenameField:
|
|
||||||
// sync
|
|
||||||
default:
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
179
vendor/github.com/graph-gophers/graphql-go/internal/exec/subscribe.go
generated
vendored
179
vendor/github.com/graph-gophers/graphql-go/internal/exec/subscribe.go
generated
vendored
@ -1,179 +0,0 @@
|
|||||||
package exec
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/graph-gophers/graphql-go/errors"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/exec/resolvable"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/exec/selected"
|
|
||||||
"github.com/graph-gophers/graphql-go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Response struct {
|
|
||||||
Data json.RawMessage
|
|
||||||
Errors []*errors.QueryError
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Request) Subscribe(ctx context.Context, s *resolvable.Schema, op *types.OperationDefinition) <-chan *Response {
|
|
||||||
var result reflect.Value
|
|
||||||
var f *fieldToExec
|
|
||||||
var err *errors.QueryError
|
|
||||||
func() {
|
|
||||||
defer r.handlePanic(ctx)
|
|
||||||
|
|
||||||
sels := selected.ApplyOperation(&r.Request, s, op)
|
|
||||||
var fields []*fieldToExec
|
|
||||||
collectFieldsToResolve(sels, s, s.Resolver, &fields, make(map[string]*fieldToExec))
|
|
||||||
|
|
||||||
// TODO: move this check into validation.Validate
|
|
||||||
if len(fields) != 1 {
|
|
||||||
err = errors.Errorf("%s", "can subscribe to at most one subscription at a time")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
f = fields[0]
|
|
||||||
|
|
||||||
var in []reflect.Value
|
|
||||||
if f.field.HasContext {
|
|
||||||
in = append(in, reflect.ValueOf(ctx))
|
|
||||||
}
|
|
||||||
if f.field.ArgsPacker != nil {
|
|
||||||
in = append(in, f.field.PackedArgs)
|
|
||||||
}
|
|
||||||
callOut := f.resolver.Method(f.field.MethodIndex).Call(in)
|
|
||||||
result = callOut[0]
|
|
||||||
|
|
||||||
if f.field.HasError && !callOut[1].IsNil() {
|
|
||||||
switch resolverErr := callOut[1].Interface().(type) {
|
|
||||||
case *errors.QueryError:
|
|
||||||
err = resolverErr
|
|
||||||
case error:
|
|
||||||
err = errors.Errorf("%s", resolverErr)
|
|
||||||
err.ResolverError = resolverErr
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("can only deal with *QueryError and error types, got %T", resolverErr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Handles the case where the locally executed func above panicked
|
|
||||||
if len(r.Request.Errs) > 0 {
|
|
||||||
return sendAndReturnClosed(&Response{Errors: r.Request.Errs})
|
|
||||||
}
|
|
||||||
|
|
||||||
if f == nil {
|
|
||||||
return sendAndReturnClosed(&Response{Errors: []*errors.QueryError{err}})
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if _, nonNullChild := f.field.Type.(*types.NonNull); nonNullChild {
|
|
||||||
return sendAndReturnClosed(&Response{Errors: []*errors.QueryError{err}})
|
|
||||||
}
|
|
||||||
return sendAndReturnClosed(&Response{Data: []byte(fmt.Sprintf(`{"%s":null}`, f.field.Alias)), Errors: []*errors.QueryError{err}})
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctxErr := ctx.Err(); ctxErr != nil {
|
|
||||||
return sendAndReturnClosed(&Response{Errors: []*errors.QueryError{errors.Errorf("%s", ctxErr)}})
|
|
||||||
}
|
|
||||||
|
|
||||||
c := make(chan *Response)
|
|
||||||
// TODO: handle resolver nil channel better?
|
|
||||||
if result.IsZero() {
|
|
||||||
close(c)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
// Check subscription context
|
|
||||||
chosen, resp, ok := reflect.Select([]reflect.SelectCase{
|
|
||||||
{
|
|
||||||
Dir: reflect.SelectRecv,
|
|
||||||
Chan: reflect.ValueOf(ctx.Done()),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Dir: reflect.SelectRecv,
|
|
||||||
Chan: result,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
switch chosen {
|
|
||||||
// subscription context done
|
|
||||||
case 0:
|
|
||||||
close(c)
|
|
||||||
return
|
|
||||||
// upstream received
|
|
||||||
case 1:
|
|
||||||
// upstream closed
|
|
||||||
if !ok {
|
|
||||||
close(c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
subR := &Request{
|
|
||||||
Request: selected.Request{
|
|
||||||
Doc: r.Request.Doc,
|
|
||||||
Vars: r.Request.Vars,
|
|
||||||
Schema: r.Request.Schema,
|
|
||||||
},
|
|
||||||
Limiter: r.Limiter,
|
|
||||||
Tracer: r.Tracer,
|
|
||||||
Logger: r.Logger,
|
|
||||||
}
|
|
||||||
var out bytes.Buffer
|
|
||||||
func() {
|
|
||||||
timeout := r.SubscribeResolverTimeout
|
|
||||||
if timeout == 0 {
|
|
||||||
timeout = time.Second
|
|
||||||
}
|
|
||||||
|
|
||||||
subCtx, cancel := context.WithTimeout(ctx, timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
// resolve response
|
|
||||||
func() {
|
|
||||||
defer subR.handlePanic(subCtx)
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
subR.execSelectionSet(subCtx, f.sels, f.field.Type, &pathSegment{nil, f.field.Alias}, s, resp, &buf)
|
|
||||||
|
|
||||||
propagateChildError := false
|
|
||||||
if _, nonNullChild := f.field.Type.(*types.NonNull); nonNullChild && resolvedToNull(&buf) {
|
|
||||||
propagateChildError = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !propagateChildError {
|
|
||||||
out.WriteString(fmt.Sprintf(`{"%s":`, f.field.Alias))
|
|
||||||
out.Write(buf.Bytes())
|
|
||||||
out.WriteString(`}`)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := subCtx.Err(); err != nil {
|
|
||||||
c <- &Response{Errors: []*errors.QueryError{errors.Errorf("%s", err)}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send response within timeout
|
|
||||||
// TODO: maybe block until sent?
|
|
||||||
select {
|
|
||||||
case <-subCtx.Done():
|
|
||||||
case c <- &Response{Data: out.Bytes(), Errors: subR.Errs}:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendAndReturnClosed(resp *Response) chan *Response {
|
|
||||||
c := make(chan *Response, 1)
|
|
||||||
c <- resp
|
|
||||||
close(c)
|
|
||||||
return c
|
|
||||||
}
|
|
156
vendor/github.com/graph-gophers/graphql-go/internal/query/query.go
generated
vendored
156
vendor/github.com/graph-gophers/graphql-go/internal/query/query.go
generated
vendored
@ -1,156 +0,0 @@
|
|||||||
package query
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"text/scanner"
|
|
||||||
|
|
||||||
"github.com/graph-gophers/graphql-go/errors"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/common"
|
|
||||||
"github.com/graph-gophers/graphql-go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
Query types.OperationType = "QUERY"
|
|
||||||
Mutation types.OperationType = "MUTATION"
|
|
||||||
Subscription types.OperationType = "SUBSCRIPTION"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Parse(queryString string) (*types.ExecutableDefinition, *errors.QueryError) {
|
|
||||||
l := common.NewLexer(queryString, false)
|
|
||||||
|
|
||||||
var execDef *types.ExecutableDefinition
|
|
||||||
err := l.CatchSyntaxError(func() { execDef = parseExecutableDefinition(l) })
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return execDef, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseExecutableDefinition(l *common.Lexer) *types.ExecutableDefinition {
|
|
||||||
ed := &types.ExecutableDefinition{}
|
|
||||||
l.ConsumeWhitespace()
|
|
||||||
for l.Peek() != scanner.EOF {
|
|
||||||
if l.Peek() == '{' {
|
|
||||||
op := &types.OperationDefinition{Type: Query, Loc: l.Location()}
|
|
||||||
op.Selections = parseSelectionSet(l)
|
|
||||||
ed.Operations = append(ed.Operations, op)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
loc := l.Location()
|
|
||||||
switch x := l.ConsumeIdent(); x {
|
|
||||||
case "query":
|
|
||||||
op := parseOperation(l, Query)
|
|
||||||
op.Loc = loc
|
|
||||||
ed.Operations = append(ed.Operations, op)
|
|
||||||
|
|
||||||
case "mutation":
|
|
||||||
ed.Operations = append(ed.Operations, parseOperation(l, Mutation))
|
|
||||||
|
|
||||||
case "subscription":
|
|
||||||
ed.Operations = append(ed.Operations, parseOperation(l, Subscription))
|
|
||||||
|
|
||||||
case "fragment":
|
|
||||||
frag := parseFragment(l)
|
|
||||||
frag.Loc = loc
|
|
||||||
ed.Fragments = append(ed.Fragments, frag)
|
|
||||||
|
|
||||||
default:
|
|
||||||
l.SyntaxError(fmt.Sprintf(`unexpected %q, expecting "fragment"`, x))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ed
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseOperation(l *common.Lexer, opType types.OperationType) *types.OperationDefinition {
|
|
||||||
op := &types.OperationDefinition{Type: opType}
|
|
||||||
op.Name.Loc = l.Location()
|
|
||||||
if l.Peek() == scanner.Ident {
|
|
||||||
op.Name = l.ConsumeIdentWithLoc()
|
|
||||||
}
|
|
||||||
op.Directives = common.ParseDirectives(l)
|
|
||||||
if l.Peek() == '(' {
|
|
||||||
l.ConsumeToken('(')
|
|
||||||
for l.Peek() != ')' {
|
|
||||||
loc := l.Location()
|
|
||||||
l.ConsumeToken('$')
|
|
||||||
iv := common.ParseInputValue(l)
|
|
||||||
iv.Loc = loc
|
|
||||||
op.Vars = append(op.Vars, iv)
|
|
||||||
}
|
|
||||||
l.ConsumeToken(')')
|
|
||||||
}
|
|
||||||
op.Selections = parseSelectionSet(l)
|
|
||||||
return op
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseFragment(l *common.Lexer) *types.FragmentDefinition {
|
|
||||||
f := &types.FragmentDefinition{}
|
|
||||||
f.Name = l.ConsumeIdentWithLoc()
|
|
||||||
l.ConsumeKeyword("on")
|
|
||||||
f.On = types.TypeName{Ident: l.ConsumeIdentWithLoc()}
|
|
||||||
f.Directives = common.ParseDirectives(l)
|
|
||||||
f.Selections = parseSelectionSet(l)
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSelectionSet(l *common.Lexer) []types.Selection {
|
|
||||||
var sels []types.Selection
|
|
||||||
l.ConsumeToken('{')
|
|
||||||
for l.Peek() != '}' {
|
|
||||||
sels = append(sels, parseSelection(l))
|
|
||||||
}
|
|
||||||
l.ConsumeToken('}')
|
|
||||||
return sels
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSelection(l *common.Lexer) types.Selection {
|
|
||||||
if l.Peek() == '.' {
|
|
||||||
return parseSpread(l)
|
|
||||||
}
|
|
||||||
return parseFieldDef(l)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseFieldDef(l *common.Lexer) *types.Field {
|
|
||||||
f := &types.Field{}
|
|
||||||
f.Alias = l.ConsumeIdentWithLoc()
|
|
||||||
f.Name = f.Alias
|
|
||||||
if l.Peek() == ':' {
|
|
||||||
l.ConsumeToken(':')
|
|
||||||
f.Name = l.ConsumeIdentWithLoc()
|
|
||||||
}
|
|
||||||
if l.Peek() == '(' {
|
|
||||||
f.Arguments = common.ParseArgumentList(l)
|
|
||||||
}
|
|
||||||
f.Directives = common.ParseDirectives(l)
|
|
||||||
if l.Peek() == '{' {
|
|
||||||
f.SelectionSetLoc = l.Location()
|
|
||||||
f.SelectionSet = parseSelectionSet(l)
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSpread(l *common.Lexer) types.Selection {
|
|
||||||
loc := l.Location()
|
|
||||||
l.ConsumeToken('.')
|
|
||||||
l.ConsumeToken('.')
|
|
||||||
l.ConsumeToken('.')
|
|
||||||
|
|
||||||
f := &types.InlineFragment{Loc: loc}
|
|
||||||
if l.Peek() == scanner.Ident {
|
|
||||||
ident := l.ConsumeIdentWithLoc()
|
|
||||||
if ident.Name != "on" {
|
|
||||||
fs := &types.FragmentSpread{
|
|
||||||
Name: ident,
|
|
||||||
Loc: loc,
|
|
||||||
}
|
|
||||||
fs.Directives = common.ParseDirectives(l)
|
|
||||||
return fs
|
|
||||||
}
|
|
||||||
f.On = types.TypeName{Ident: l.ConsumeIdentWithLoc()}
|
|
||||||
}
|
|
||||||
f.Directives = common.ParseDirectives(l)
|
|
||||||
f.Selections = parseSelectionSet(l)
|
|
||||||
return f
|
|
||||||
}
|
|
203
vendor/github.com/graph-gophers/graphql-go/internal/schema/meta.go
generated
vendored
203
vendor/github.com/graph-gophers/graphql-go/internal/schema/meta.go
generated
vendored
@ -1,203 +0,0 @@
|
|||||||
package schema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/graph-gophers/graphql-go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
_ = newMeta()
|
|
||||||
}
|
|
||||||
|
|
||||||
// newMeta initializes an instance of the meta Schema.
|
|
||||||
func newMeta() *types.Schema {
|
|
||||||
s := &types.Schema{
|
|
||||||
EntryPointNames: make(map[string]string),
|
|
||||||
Types: make(map[string]types.NamedType),
|
|
||||||
Directives: make(map[string]*types.DirectiveDefinition),
|
|
||||||
}
|
|
||||||
|
|
||||||
err := Parse(s, metaSrc, false)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
var metaSrc = `
|
|
||||||
# The ` + "`" + `Int` + "`" + ` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
|
|
||||||
scalar Int
|
|
||||||
|
|
||||||
# The ` + "`" + `Float` + "`" + ` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point).
|
|
||||||
scalar Float
|
|
||||||
|
|
||||||
# The ` + "`" + `String` + "`" + ` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.
|
|
||||||
scalar String
|
|
||||||
|
|
||||||
# The ` + "`" + `Boolean` + "`" + ` scalar type represents ` + "`" + `true` + "`" + ` or ` + "`" + `false` + "`" + `.
|
|
||||||
scalar Boolean
|
|
||||||
|
|
||||||
# The ` + "`" + `ID` + "`" + ` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as ` + "`" + `"4"` + "`" + `) or integer (such as ` + "`" + `4` + "`" + `) input value will be accepted as an ID.
|
|
||||||
scalar ID
|
|
||||||
|
|
||||||
# Directs the executor to include this field or fragment only when the ` + "`" + `if` + "`" + ` argument is true.
|
|
||||||
directive @include(
|
|
||||||
# Included when true.
|
|
||||||
if: Boolean!
|
|
||||||
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
|
||||||
|
|
||||||
# Directs the executor to skip this field or fragment when the ` + "`" + `if` + "`" + ` argument is true.
|
|
||||||
directive @skip(
|
|
||||||
# Skipped when true.
|
|
||||||
if: Boolean!
|
|
||||||
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
|
||||||
|
|
||||||
# Marks an element of a GraphQL schema as no longer supported.
|
|
||||||
directive @deprecated(
|
|
||||||
# Explains why this element was deprecated, usually also including a suggestion
|
|
||||||
# for how to access supported similar data. Formatted in
|
|
||||||
# [Markdown](https://daringfireball.net/projects/markdown/).
|
|
||||||
reason: String = "No longer supported"
|
|
||||||
) on FIELD_DEFINITION | ENUM_VALUE
|
|
||||||
|
|
||||||
# A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
|
|
||||||
#
|
|
||||||
# In some cases, you need to provide options to alter GraphQL's execution behavior
|
|
||||||
# in ways field arguments will not suffice, such as conditionally including or
|
|
||||||
# skipping a field. Directives provide this by describing additional information
|
|
||||||
# to the executor.
|
|
||||||
type __Directive {
|
|
||||||
name: String!
|
|
||||||
description: String
|
|
||||||
locations: [__DirectiveLocation!]!
|
|
||||||
args: [__InputValue!]!
|
|
||||||
}
|
|
||||||
|
|
||||||
# A Directive can be adjacent to many parts of the GraphQL language, a
|
|
||||||
# __DirectiveLocation describes one such possible adjacencies.
|
|
||||||
enum __DirectiveLocation {
|
|
||||||
# Location adjacent to a query operation.
|
|
||||||
QUERY
|
|
||||||
# Location adjacent to a mutation operation.
|
|
||||||
MUTATION
|
|
||||||
# Location adjacent to a subscription operation.
|
|
||||||
SUBSCRIPTION
|
|
||||||
# Location adjacent to a field.
|
|
||||||
FIELD
|
|
||||||
# Location adjacent to a fragment definition.
|
|
||||||
FRAGMENT_DEFINITION
|
|
||||||
# Location adjacent to a fragment spread.
|
|
||||||
FRAGMENT_SPREAD
|
|
||||||
# Location adjacent to an inline fragment.
|
|
||||||
INLINE_FRAGMENT
|
|
||||||
# Location adjacent to a schema definition.
|
|
||||||
SCHEMA
|
|
||||||
# Location adjacent to a scalar definition.
|
|
||||||
SCALAR
|
|
||||||
# Location adjacent to an object type definition.
|
|
||||||
OBJECT
|
|
||||||
# Location adjacent to a field definition.
|
|
||||||
FIELD_DEFINITION
|
|
||||||
# Location adjacent to an argument definition.
|
|
||||||
ARGUMENT_DEFINITION
|
|
||||||
# Location adjacent to an interface definition.
|
|
||||||
INTERFACE
|
|
||||||
# Location adjacent to a union definition.
|
|
||||||
UNION
|
|
||||||
# Location adjacent to an enum definition.
|
|
||||||
ENUM
|
|
||||||
# Location adjacent to an enum value definition.
|
|
||||||
ENUM_VALUE
|
|
||||||
# Location adjacent to an input object type definition.
|
|
||||||
INPUT_OBJECT
|
|
||||||
# Location adjacent to an input object field definition.
|
|
||||||
INPUT_FIELD_DEFINITION
|
|
||||||
}
|
|
||||||
|
|
||||||
# One possible value for a given Enum. Enum values are unique values, not a
|
|
||||||
# placeholder for a string or numeric value. However an Enum value is returned in
|
|
||||||
# a JSON response as a string.
|
|
||||||
type __EnumValue {
|
|
||||||
name: String!
|
|
||||||
description: String
|
|
||||||
isDeprecated: Boolean!
|
|
||||||
deprecationReason: String
|
|
||||||
}
|
|
||||||
|
|
||||||
# Object and Interface types are described by a list of Fields, each of which has
|
|
||||||
# a name, potentially a list of arguments, and a return type.
|
|
||||||
type __Field {
|
|
||||||
name: String!
|
|
||||||
description: String
|
|
||||||
args: [__InputValue!]!
|
|
||||||
type: __Type!
|
|
||||||
isDeprecated: Boolean!
|
|
||||||
deprecationReason: String
|
|
||||||
}
|
|
||||||
|
|
||||||
# Arguments provided to Fields or Directives and the input fields of an
|
|
||||||
# InputObject are represented as Input Values which describe their type and
|
|
||||||
# optionally a default value.
|
|
||||||
type __InputValue {
|
|
||||||
name: String!
|
|
||||||
description: String
|
|
||||||
type: __Type!
|
|
||||||
# A GraphQL-formatted string representing the default value for this input value.
|
|
||||||
defaultValue: String
|
|
||||||
}
|
|
||||||
|
|
||||||
# A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all
|
|
||||||
# available types and directives on the server, as well as the entry points for
|
|
||||||
# query, mutation, and subscription operations.
|
|
||||||
type __Schema {
|
|
||||||
# A list of all types supported by this server.
|
|
||||||
types: [__Type!]!
|
|
||||||
# The type that query operations will be rooted at.
|
|
||||||
queryType: __Type!
|
|
||||||
# If this server supports mutation, the type that mutation operations will be rooted at.
|
|
||||||
mutationType: __Type
|
|
||||||
# If this server support subscription, the type that subscription operations will be rooted at.
|
|
||||||
subscriptionType: __Type
|
|
||||||
# A list of all directives supported by this server.
|
|
||||||
directives: [__Directive!]!
|
|
||||||
}
|
|
||||||
|
|
||||||
# The fundamental unit of any GraphQL Schema is the type. There are many kinds of
|
|
||||||
# types in GraphQL as represented by the ` + "`" + `__TypeKind` + "`" + ` enum.
|
|
||||||
#
|
|
||||||
# Depending on the kind of a type, certain fields describe information about that
|
|
||||||
# type. Scalar types provide no information beyond a name and description, while
|
|
||||||
# Enum types provide their values. Object and Interface types provide the fields
|
|
||||||
# they describe. Abstract types, Union and Interface, provide the Object types
|
|
||||||
# possible at runtime. List and NonNull types compose other types.
|
|
||||||
type __Type {
|
|
||||||
kind: __TypeKind!
|
|
||||||
name: String
|
|
||||||
description: String
|
|
||||||
fields(includeDeprecated: Boolean = false): [__Field!]
|
|
||||||
interfaces: [__Type!]
|
|
||||||
possibleTypes: [__Type!]
|
|
||||||
enumValues(includeDeprecated: Boolean = false): [__EnumValue!]
|
|
||||||
inputFields: [__InputValue!]
|
|
||||||
ofType: __Type
|
|
||||||
}
|
|
||||||
|
|
||||||
# An enum describing what kind of type a given ` + "`" + `__Type` + "`" + ` is.
|
|
||||||
enum __TypeKind {
|
|
||||||
# Indicates this type is a scalar.
|
|
||||||
SCALAR
|
|
||||||
# Indicates this type is an object. ` + "`" + `fields` + "`" + ` and ` + "`" + `interfaces` + "`" + ` are valid fields.
|
|
||||||
OBJECT
|
|
||||||
# Indicates this type is an interface. ` + "`" + `fields` + "`" + ` and ` + "`" + `possibleTypes` + "`" + ` are valid fields.
|
|
||||||
INTERFACE
|
|
||||||
# Indicates this type is a union. ` + "`" + `possibleTypes` + "`" + ` is a valid field.
|
|
||||||
UNION
|
|
||||||
# Indicates this type is an enum. ` + "`" + `enumValues` + "`" + ` is a valid field.
|
|
||||||
ENUM
|
|
||||||
# Indicates this type is an input object. ` + "`" + `inputFields` + "`" + ` is a valid field.
|
|
||||||
INPUT_OBJECT
|
|
||||||
# Indicates this type is a list. ` + "`" + `ofType` + "`" + ` is a valid field.
|
|
||||||
LIST
|
|
||||||
# Indicates this type is a non-null. ` + "`" + `ofType` + "`" + ` is a valid field.
|
|
||||||
NON_NULL
|
|
||||||
}
|
|
||||||
`
|
|
586
vendor/github.com/graph-gophers/graphql-go/internal/schema/schema.go
generated
vendored
586
vendor/github.com/graph-gophers/graphql-go/internal/schema/schema.go
generated
vendored
@ -1,586 +0,0 @@
|
|||||||
package schema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"text/scanner"
|
|
||||||
|
|
||||||
"github.com/graph-gophers/graphql-go/errors"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/common"
|
|
||||||
"github.com/graph-gophers/graphql-go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// New initializes an instance of Schema.
|
|
||||||
func New() *types.Schema {
|
|
||||||
s := &types.Schema{
|
|
||||||
EntryPointNames: make(map[string]string),
|
|
||||||
Types: make(map[string]types.NamedType),
|
|
||||||
Directives: make(map[string]*types.DirectiveDefinition),
|
|
||||||
}
|
|
||||||
m := newMeta()
|
|
||||||
for n, t := range m.Types {
|
|
||||||
s.Types[n] = t
|
|
||||||
}
|
|
||||||
for n, d := range m.Directives {
|
|
||||||
s.Directives[n] = d
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func Parse(s *types.Schema, schemaString string, useStringDescriptions bool) error {
|
|
||||||
l := common.NewLexer(schemaString, useStringDescriptions)
|
|
||||||
err := l.CatchSyntaxError(func() { parseSchema(s, l) })
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mergeExtensions(s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, t := range s.Types {
|
|
||||||
if err := resolveNamedType(s, t); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, d := range s.Directives {
|
|
||||||
for _, arg := range d.Arguments {
|
|
||||||
t, err := common.ResolveType(arg.Type, s.Resolve)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
arg.Type = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://graphql.github.io/graphql-spec/June2018/#sec-Root-Operation-Types
|
|
||||||
// > While any type can be the root operation type for a GraphQL operation, the type system definition language can
|
|
||||||
// > omit the schema definition when the query, mutation, and subscription root types are named Query, Mutation,
|
|
||||||
// > and Subscription respectively.
|
|
||||||
if len(s.EntryPointNames) == 0 {
|
|
||||||
if _, ok := s.Types["Query"]; ok {
|
|
||||||
s.EntryPointNames["query"] = "Query"
|
|
||||||
}
|
|
||||||
if _, ok := s.Types["Mutation"]; ok {
|
|
||||||
s.EntryPointNames["mutation"] = "Mutation"
|
|
||||||
}
|
|
||||||
if _, ok := s.Types["Subscription"]; ok {
|
|
||||||
s.EntryPointNames["subscription"] = "Subscription"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.EntryPoints = make(map[string]types.NamedType)
|
|
||||||
for key, name := range s.EntryPointNames {
|
|
||||||
t, ok := s.Types[name]
|
|
||||||
if !ok {
|
|
||||||
return errors.Errorf("type %q not found", name)
|
|
||||||
}
|
|
||||||
s.EntryPoints[key] = t
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface types need validation: https://spec.graphql.org/draft/#sec-Interfaces.Interfaces-Implementing-Interfaces
|
|
||||||
for _, typeDef := range s.Types {
|
|
||||||
switch t := typeDef.(type) {
|
|
||||||
case *types.InterfaceTypeDefinition:
|
|
||||||
for i, implements := range t.Interfaces {
|
|
||||||
typ, ok := s.Types[implements.Name]
|
|
||||||
if !ok {
|
|
||||||
return errors.Errorf("interface %q not found", implements)
|
|
||||||
}
|
|
||||||
inteface, ok := typ.(*types.InterfaceTypeDefinition)
|
|
||||||
if !ok {
|
|
||||||
return errors.Errorf("type %q is not an interface", inteface)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range inteface.Fields.Names() {
|
|
||||||
if t.Fields.Get(f) == nil {
|
|
||||||
return errors.Errorf("interface %q expects field %q but %q does not provide it", inteface.Name, f, t.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Interfaces[i] = inteface
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, obj := range s.Objects {
|
|
||||||
obj.Interfaces = make([]*types.InterfaceTypeDefinition, len(obj.InterfaceNames))
|
|
||||||
if err := resolveDirectives(s, obj.Directives, "OBJECT"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, field := range obj.Fields {
|
|
||||||
if err := resolveDirectives(s, field.Directives, "FIELD_DEFINITION"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i, intfName := range obj.InterfaceNames {
|
|
||||||
t, ok := s.Types[intfName]
|
|
||||||
if !ok {
|
|
||||||
return errors.Errorf("interface %q not found", intfName)
|
|
||||||
}
|
|
||||||
intf, ok := t.(*types.InterfaceTypeDefinition)
|
|
||||||
if !ok {
|
|
||||||
return errors.Errorf("type %q is not an interface", intfName)
|
|
||||||
}
|
|
||||||
for _, f := range intf.Fields.Names() {
|
|
||||||
if obj.Fields.Get(f) == nil {
|
|
||||||
return errors.Errorf("interface %q expects field %q but %q does not provide it", intfName, f, obj.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
obj.Interfaces[i] = intf
|
|
||||||
intf.PossibleTypes = append(intf.PossibleTypes, obj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, union := range s.Unions {
|
|
||||||
if err := resolveDirectives(s, union.Directives, "UNION"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
union.UnionMemberTypes = make([]*types.ObjectTypeDefinition, len(union.TypeNames))
|
|
||||||
for i, name := range union.TypeNames {
|
|
||||||
t, ok := s.Types[name]
|
|
||||||
if !ok {
|
|
||||||
return errors.Errorf("object type %q not found", name)
|
|
||||||
}
|
|
||||||
obj, ok := t.(*types.ObjectTypeDefinition)
|
|
||||||
if !ok {
|
|
||||||
return errors.Errorf("type %q is not an object", name)
|
|
||||||
}
|
|
||||||
union.UnionMemberTypes[i] = obj
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, enum := range s.Enums {
|
|
||||||
if err := resolveDirectives(s, enum.Directives, "ENUM"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, value := range enum.EnumValuesDefinition {
|
|
||||||
if err := resolveDirectives(s, value.Directives, "ENUM_VALUE"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseSchema(schemaString string, useStringDescriptions bool) (*types.Schema, error) {
|
|
||||||
s := New()
|
|
||||||
err := Parse(s, schemaString, useStringDescriptions)
|
|
||||||
return s, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func mergeExtensions(s *types.Schema) error {
|
|
||||||
for _, ext := range s.Extensions {
|
|
||||||
typ := s.Types[ext.Type.TypeName()]
|
|
||||||
if typ == nil {
|
|
||||||
return fmt.Errorf("trying to extend unknown type %q", ext.Type.TypeName())
|
|
||||||
}
|
|
||||||
|
|
||||||
if typ.Kind() != ext.Type.Kind() {
|
|
||||||
return fmt.Errorf("trying to extend type %q with type %q", typ.Kind(), ext.Type.Kind())
|
|
||||||
}
|
|
||||||
|
|
||||||
switch og := typ.(type) {
|
|
||||||
case *types.ObjectTypeDefinition:
|
|
||||||
e := ext.Type.(*types.ObjectTypeDefinition)
|
|
||||||
|
|
||||||
for _, field := range e.Fields {
|
|
||||||
if og.Fields.Get(field.Name) != nil {
|
|
||||||
return fmt.Errorf("extended field %q already exists", field.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
og.Fields = append(og.Fields, e.Fields...)
|
|
||||||
|
|
||||||
for _, en := range e.InterfaceNames {
|
|
||||||
for _, on := range og.InterfaceNames {
|
|
||||||
if on == en {
|
|
||||||
return fmt.Errorf("interface %q implemented in the extension is already implemented in %q", on, og.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
og.InterfaceNames = append(og.InterfaceNames, e.InterfaceNames...)
|
|
||||||
|
|
||||||
case *types.InputObject:
|
|
||||||
e := ext.Type.(*types.InputObject)
|
|
||||||
|
|
||||||
for _, field := range e.Values {
|
|
||||||
if og.Values.Get(field.Name.Name) != nil {
|
|
||||||
return fmt.Errorf("extended field %q already exists", field.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
og.Values = append(og.Values, e.Values...)
|
|
||||||
|
|
||||||
case *types.InterfaceTypeDefinition:
|
|
||||||
e := ext.Type.(*types.InterfaceTypeDefinition)
|
|
||||||
|
|
||||||
for _, field := range e.Fields {
|
|
||||||
if og.Fields.Get(field.Name) != nil {
|
|
||||||
return fmt.Errorf("extended field %s already exists", field.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
og.Fields = append(og.Fields, e.Fields...)
|
|
||||||
|
|
||||||
case *types.Union:
|
|
||||||
e := ext.Type.(*types.Union)
|
|
||||||
|
|
||||||
for _, en := range e.TypeNames {
|
|
||||||
for _, on := range og.TypeNames {
|
|
||||||
if on == en {
|
|
||||||
return fmt.Errorf("union type %q already declared in %q", on, og.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
og.TypeNames = append(og.TypeNames, e.TypeNames...)
|
|
||||||
|
|
||||||
case *types.EnumTypeDefinition:
|
|
||||||
e := ext.Type.(*types.EnumTypeDefinition)
|
|
||||||
|
|
||||||
for _, en := range e.EnumValuesDefinition {
|
|
||||||
for _, on := range og.EnumValuesDefinition {
|
|
||||||
if on.EnumValue == en.EnumValue {
|
|
||||||
return fmt.Errorf("enum value %q already declared in %q", on.EnumValue, og.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
og.EnumValuesDefinition = append(og.EnumValuesDefinition, e.EnumValuesDefinition...)
|
|
||||||
default:
|
|
||||||
return fmt.Errorf(`unexpected %q, expecting "schema", "type", "enum", "interface", "union" or "input"`, og.TypeName())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveNamedType(s *types.Schema, t types.NamedType) error {
|
|
||||||
switch t := t.(type) {
|
|
||||||
case *types.ObjectTypeDefinition:
|
|
||||||
for _, f := range t.Fields {
|
|
||||||
if err := resolveField(s, f); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *types.InterfaceTypeDefinition:
|
|
||||||
for _, f := range t.Fields {
|
|
||||||
if err := resolveField(s, f); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *types.InputObject:
|
|
||||||
if err := resolveInputObject(s, t.Values); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveField(s *types.Schema, f *types.FieldDefinition) error {
|
|
||||||
t, err := common.ResolveType(f.Type, s.Resolve)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
f.Type = t
|
|
||||||
if err := resolveDirectives(s, f.Directives, "FIELD_DEFINITION"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return resolveInputObject(s, f.Arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveDirectives(s *types.Schema, directives types.DirectiveList, loc string) error {
|
|
||||||
for _, d := range directives {
|
|
||||||
dirName := d.Name.Name
|
|
||||||
dd, ok := s.Directives[dirName]
|
|
||||||
if !ok {
|
|
||||||
return errors.Errorf("directive %q not found", dirName)
|
|
||||||
}
|
|
||||||
validLoc := false
|
|
||||||
for _, l := range dd.Locations {
|
|
||||||
if l == loc {
|
|
||||||
validLoc = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !validLoc {
|
|
||||||
return errors.Errorf("invalid location %q for directive %q (must be one of %v)", loc, dirName, dd.Locations)
|
|
||||||
}
|
|
||||||
for _, arg := range d.Arguments {
|
|
||||||
if dd.Arguments.Get(arg.Name.Name) == nil {
|
|
||||||
return errors.Errorf("invalid argument %q for directive %q", arg.Name.Name, dirName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, arg := range dd.Arguments {
|
|
||||||
if _, ok := d.Arguments.Get(arg.Name.Name); !ok {
|
|
||||||
d.Arguments = append(d.Arguments, &types.Argument{Name: arg.Name, Value: arg.Default})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveInputObject(s *types.Schema, values types.ArgumentsDefinition) error {
|
|
||||||
for _, v := range values {
|
|
||||||
t, err := common.ResolveType(v.Type, s.Resolve)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v.Type = t
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSchema(s *types.Schema, l *common.Lexer) {
|
|
||||||
l.ConsumeWhitespace()
|
|
||||||
|
|
||||||
for l.Peek() != scanner.EOF {
|
|
||||||
desc := l.DescComment()
|
|
||||||
switch x := l.ConsumeIdent(); x {
|
|
||||||
|
|
||||||
case "schema":
|
|
||||||
l.ConsumeToken('{')
|
|
||||||
for l.Peek() != '}' {
|
|
||||||
|
|
||||||
name := l.ConsumeIdent()
|
|
||||||
l.ConsumeToken(':')
|
|
||||||
typ := l.ConsumeIdent()
|
|
||||||
s.EntryPointNames[name] = typ
|
|
||||||
}
|
|
||||||
l.ConsumeToken('}')
|
|
||||||
|
|
||||||
case "type":
|
|
||||||
obj := parseObjectDef(l)
|
|
||||||
obj.Desc = desc
|
|
||||||
s.Types[obj.Name] = obj
|
|
||||||
s.Objects = append(s.Objects, obj)
|
|
||||||
|
|
||||||
case "interface":
|
|
||||||
iface := parseInterfaceDef(l)
|
|
||||||
iface.Desc = desc
|
|
||||||
s.Types[iface.Name] = iface
|
|
||||||
|
|
||||||
case "union":
|
|
||||||
union := parseUnionDef(l)
|
|
||||||
union.Desc = desc
|
|
||||||
s.Types[union.Name] = union
|
|
||||||
s.Unions = append(s.Unions, union)
|
|
||||||
|
|
||||||
case "enum":
|
|
||||||
enum := parseEnumDef(l)
|
|
||||||
enum.Desc = desc
|
|
||||||
s.Types[enum.Name] = enum
|
|
||||||
s.Enums = append(s.Enums, enum)
|
|
||||||
|
|
||||||
case "input":
|
|
||||||
input := parseInputDef(l)
|
|
||||||
input.Desc = desc
|
|
||||||
s.Types[input.Name] = input
|
|
||||||
|
|
||||||
case "scalar":
|
|
||||||
loc := l.Location()
|
|
||||||
name := l.ConsumeIdent()
|
|
||||||
directives := common.ParseDirectives(l)
|
|
||||||
s.Types[name] = &types.ScalarTypeDefinition{Name: name, Desc: desc, Directives: directives, Loc: loc}
|
|
||||||
|
|
||||||
case "directive":
|
|
||||||
directive := parseDirectiveDef(l)
|
|
||||||
directive.Desc = desc
|
|
||||||
s.Directives[directive.Name] = directive
|
|
||||||
|
|
||||||
case "extend":
|
|
||||||
parseExtension(s, l)
|
|
||||||
|
|
||||||
default:
|
|
||||||
// TODO: Add support for type extensions.
|
|
||||||
l.SyntaxError(fmt.Sprintf(`unexpected %q, expecting "schema", "type", "enum", "interface", "union", "input", "scalar" or "directive"`, x))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseObjectDef(l *common.Lexer) *types.ObjectTypeDefinition {
|
|
||||||
object := &types.ObjectTypeDefinition{Loc: l.Location(), Name: l.ConsumeIdent()}
|
|
||||||
|
|
||||||
for {
|
|
||||||
if l.Peek() == '{' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if l.Peek() == '@' {
|
|
||||||
object.Directives = common.ParseDirectives(l)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if l.Peek() == scanner.Ident {
|
|
||||||
l.ConsumeKeyword("implements")
|
|
||||||
|
|
||||||
for l.Peek() != '{' && l.Peek() != '@' {
|
|
||||||
if l.Peek() == '&' {
|
|
||||||
l.ConsumeToken('&')
|
|
||||||
}
|
|
||||||
|
|
||||||
object.InterfaceNames = append(object.InterfaceNames, l.ConsumeIdent())
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
l.ConsumeToken('{')
|
|
||||||
object.Fields = parseFieldsDef(l)
|
|
||||||
l.ConsumeToken('}')
|
|
||||||
|
|
||||||
return object
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseInterfaceDef(l *common.Lexer) *types.InterfaceTypeDefinition {
|
|
||||||
i := &types.InterfaceTypeDefinition{Loc: l.Location(), Name: l.ConsumeIdent()}
|
|
||||||
|
|
||||||
if l.Peek() == scanner.Ident {
|
|
||||||
l.ConsumeKeyword("implements")
|
|
||||||
i.Interfaces = append(i.Interfaces, &types.InterfaceTypeDefinition{Name: l.ConsumeIdent()})
|
|
||||||
|
|
||||||
for l.Peek() == '&' {
|
|
||||||
l.ConsumeToken('&')
|
|
||||||
i.Interfaces = append(i.Interfaces, &types.InterfaceTypeDefinition{Name: l.ConsumeIdent()})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i.Directives = common.ParseDirectives(l)
|
|
||||||
|
|
||||||
l.ConsumeToken('{')
|
|
||||||
i.Fields = parseFieldsDef(l)
|
|
||||||
l.ConsumeToken('}')
|
|
||||||
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseUnionDef(l *common.Lexer) *types.Union {
|
|
||||||
union := &types.Union{Loc: l.Location(), Name: l.ConsumeIdent()}
|
|
||||||
|
|
||||||
union.Directives = common.ParseDirectives(l)
|
|
||||||
l.ConsumeToken('=')
|
|
||||||
union.TypeNames = []string{l.ConsumeIdent()}
|
|
||||||
for l.Peek() == '|' {
|
|
||||||
l.ConsumeToken('|')
|
|
||||||
union.TypeNames = append(union.TypeNames, l.ConsumeIdent())
|
|
||||||
}
|
|
||||||
|
|
||||||
return union
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseInputDef(l *common.Lexer) *types.InputObject {
|
|
||||||
i := &types.InputObject{}
|
|
||||||
i.Loc = l.Location()
|
|
||||||
i.Name = l.ConsumeIdent()
|
|
||||||
i.Directives = common.ParseDirectives(l)
|
|
||||||
l.ConsumeToken('{')
|
|
||||||
for l.Peek() != '}' {
|
|
||||||
i.Values = append(i.Values, common.ParseInputValue(l))
|
|
||||||
}
|
|
||||||
l.ConsumeToken('}')
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseEnumDef(l *common.Lexer) *types.EnumTypeDefinition {
|
|
||||||
enum := &types.EnumTypeDefinition{Loc: l.Location(), Name: l.ConsumeIdent()}
|
|
||||||
|
|
||||||
enum.Directives = common.ParseDirectives(l)
|
|
||||||
l.ConsumeToken('{')
|
|
||||||
for l.Peek() != '}' {
|
|
||||||
v := &types.EnumValueDefinition{
|
|
||||||
Desc: l.DescComment(),
|
|
||||||
Loc: l.Location(),
|
|
||||||
EnumValue: l.ConsumeIdent(),
|
|
||||||
Directives: common.ParseDirectives(l),
|
|
||||||
}
|
|
||||||
|
|
||||||
enum.EnumValuesDefinition = append(enum.EnumValuesDefinition, v)
|
|
||||||
}
|
|
||||||
l.ConsumeToken('}')
|
|
||||||
return enum
|
|
||||||
}
|
|
||||||
func parseDirectiveDef(l *common.Lexer) *types.DirectiveDefinition {
|
|
||||||
l.ConsumeToken('@')
|
|
||||||
loc := l.Location()
|
|
||||||
d := &types.DirectiveDefinition{Name: l.ConsumeIdent(), Loc: loc}
|
|
||||||
|
|
||||||
if l.Peek() == '(' {
|
|
||||||
l.ConsumeToken('(')
|
|
||||||
for l.Peek() != ')' {
|
|
||||||
v := common.ParseInputValue(l)
|
|
||||||
d.Arguments = append(d.Arguments, v)
|
|
||||||
}
|
|
||||||
l.ConsumeToken(')')
|
|
||||||
}
|
|
||||||
|
|
||||||
l.ConsumeKeyword("on")
|
|
||||||
|
|
||||||
for {
|
|
||||||
loc := l.ConsumeIdent()
|
|
||||||
d.Locations = append(d.Locations, loc)
|
|
||||||
if l.Peek() != '|' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
l.ConsumeToken('|')
|
|
||||||
}
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseExtension(s *types.Schema, l *common.Lexer) {
|
|
||||||
loc := l.Location()
|
|
||||||
switch x := l.ConsumeIdent(); x {
|
|
||||||
case "schema":
|
|
||||||
l.ConsumeToken('{')
|
|
||||||
for l.Peek() != '}' {
|
|
||||||
name := l.ConsumeIdent()
|
|
||||||
l.ConsumeToken(':')
|
|
||||||
typ := l.ConsumeIdent()
|
|
||||||
s.EntryPointNames[name] = typ
|
|
||||||
}
|
|
||||||
l.ConsumeToken('}')
|
|
||||||
|
|
||||||
case "type":
|
|
||||||
obj := parseObjectDef(l)
|
|
||||||
s.Extensions = append(s.Extensions, &types.Extension{Type: obj, Loc: loc})
|
|
||||||
|
|
||||||
case "interface":
|
|
||||||
iface := parseInterfaceDef(l)
|
|
||||||
s.Extensions = append(s.Extensions, &types.Extension{Type: iface, Loc: loc})
|
|
||||||
|
|
||||||
case "union":
|
|
||||||
union := parseUnionDef(l)
|
|
||||||
s.Extensions = append(s.Extensions, &types.Extension{Type: union, Loc: loc})
|
|
||||||
|
|
||||||
case "enum":
|
|
||||||
enum := parseEnumDef(l)
|
|
||||||
s.Extensions = append(s.Extensions, &types.Extension{Type: enum, Loc: loc})
|
|
||||||
|
|
||||||
case "input":
|
|
||||||
input := parseInputDef(l)
|
|
||||||
s.Extensions = append(s.Extensions, &types.Extension{Type: input, Loc: loc})
|
|
||||||
|
|
||||||
default:
|
|
||||||
// TODO: Add ScalarTypeDefinition when adding directives
|
|
||||||
l.SyntaxError(fmt.Sprintf(`unexpected %q, expecting "schema", "type", "enum", "interface", "union" or "input"`, x))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseFieldsDef(l *common.Lexer) types.FieldsDefinition {
|
|
||||||
var fields types.FieldsDefinition
|
|
||||||
for l.Peek() != '}' {
|
|
||||||
f := &types.FieldDefinition{}
|
|
||||||
f.Desc = l.DescComment()
|
|
||||||
f.Loc = l.Location()
|
|
||||||
f.Name = l.ConsumeIdent()
|
|
||||||
if l.Peek() == '(' {
|
|
||||||
l.ConsumeToken('(')
|
|
||||||
for l.Peek() != ')' {
|
|
||||||
f.Arguments = append(f.Arguments, common.ParseInputValue(l))
|
|
||||||
}
|
|
||||||
l.ConsumeToken(')')
|
|
||||||
}
|
|
||||||
l.ConsumeToken(':')
|
|
||||||
f.Type = common.ParseType(l)
|
|
||||||
f.Directives = common.ParseDirectives(l)
|
|
||||||
fields = append(fields, f)
|
|
||||||
}
|
|
||||||
return fields
|
|
||||||
}
|
|
71
vendor/github.com/graph-gophers/graphql-go/internal/validation/suggestion.go
generated
vendored
71
vendor/github.com/graph-gophers/graphql-go/internal/validation/suggestion.go
generated
vendored
@ -1,71 +0,0 @@
|
|||||||
package validation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func makeSuggestion(prefix string, options []string, input string) string {
|
|
||||||
var selected []string
|
|
||||||
distances := make(map[string]int)
|
|
||||||
for _, opt := range options {
|
|
||||||
distance := levenshteinDistance(input, opt)
|
|
||||||
threshold := max(len(input)/2, max(len(opt)/2, 1))
|
|
||||||
if distance < threshold {
|
|
||||||
selected = append(selected, opt)
|
|
||||||
distances[opt] = distance
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(selected) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
sort.Slice(selected, func(i, j int) bool {
|
|
||||||
return distances[selected[i]] < distances[selected[j]]
|
|
||||||
})
|
|
||||||
|
|
||||||
parts := make([]string, len(selected))
|
|
||||||
for i, opt := range selected {
|
|
||||||
parts[i] = strconv.Quote(opt)
|
|
||||||
}
|
|
||||||
if len(parts) > 1 {
|
|
||||||
parts[len(parts)-1] = "or " + parts[len(parts)-1]
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(" %s %s?", prefix, strings.Join(parts, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
func levenshteinDistance(s1, s2 string) int {
|
|
||||||
column := make([]int, len(s1)+1)
|
|
||||||
for y := range s1 {
|
|
||||||
column[y+1] = y + 1
|
|
||||||
}
|
|
||||||
for x, rx := range s2 {
|
|
||||||
column[0] = x + 1
|
|
||||||
lastdiag := x
|
|
||||||
for y, ry := range s1 {
|
|
||||||
olddiag := column[y+1]
|
|
||||||
if rx != ry {
|
|
||||||
lastdiag++
|
|
||||||
}
|
|
||||||
column[y+1] = min(column[y+1]+1, min(column[y]+1, lastdiag))
|
|
||||||
lastdiag = olddiag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return column[len(s1)]
|
|
||||||
}
|
|
||||||
|
|
||||||
func min(a, b int) int {
|
|
||||||
if a < b {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func max(a, b int) int {
|
|
||||||
if a > b {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
980
vendor/github.com/graph-gophers/graphql-go/internal/validation/validation.go
generated
vendored
980
vendor/github.com/graph-gophers/graphql-go/internal/validation/validation.go
generated
vendored
@ -1,980 +0,0 @@
|
|||||||
package validation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"text/scanner"
|
|
||||||
|
|
||||||
"github.com/graph-gophers/graphql-go/errors"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/common"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/query"
|
|
||||||
"github.com/graph-gophers/graphql-go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type varSet map[*types.InputValueDefinition]struct{}
|
|
||||||
|
|
||||||
type selectionPair struct{ a, b types.Selection }
|
|
||||||
|
|
||||||
type nameSet map[string]errors.Location
|
|
||||||
|
|
||||||
type fieldInfo struct {
|
|
||||||
sf *types.FieldDefinition
|
|
||||||
parent types.NamedType
|
|
||||||
}
|
|
||||||
|
|
||||||
type context struct {
|
|
||||||
schema *types.Schema
|
|
||||||
doc *types.ExecutableDefinition
|
|
||||||
errs []*errors.QueryError
|
|
||||||
opErrs map[*types.OperationDefinition][]*errors.QueryError
|
|
||||||
usedVars map[*types.OperationDefinition]varSet
|
|
||||||
fieldMap map[*types.Field]fieldInfo
|
|
||||||
overlapValidated map[selectionPair]struct{}
|
|
||||||
maxDepth int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *context) addErr(loc errors.Location, rule string, format string, a ...interface{}) {
|
|
||||||
c.addErrMultiLoc([]errors.Location{loc}, rule, format, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *context) addErrMultiLoc(locs []errors.Location, rule string, format string, a ...interface{}) {
|
|
||||||
c.errs = append(c.errs, &errors.QueryError{
|
|
||||||
Message: fmt.Sprintf(format, a...),
|
|
||||||
Locations: locs,
|
|
||||||
Rule: rule,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type opContext struct {
|
|
||||||
*context
|
|
||||||
ops []*types.OperationDefinition
|
|
||||||
}
|
|
||||||
|
|
||||||
func newContext(s *types.Schema, doc *types.ExecutableDefinition, maxDepth int) *context {
|
|
||||||
return &context{
|
|
||||||
schema: s,
|
|
||||||
doc: doc,
|
|
||||||
opErrs: make(map[*types.OperationDefinition][]*errors.QueryError),
|
|
||||||
usedVars: make(map[*types.OperationDefinition]varSet),
|
|
||||||
fieldMap: make(map[*types.Field]fieldInfo),
|
|
||||||
overlapValidated: make(map[selectionPair]struct{}),
|
|
||||||
maxDepth: maxDepth,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Validate(s *types.Schema, doc *types.ExecutableDefinition, variables map[string]interface{}, maxDepth int) []*errors.QueryError {
|
|
||||||
c := newContext(s, doc, maxDepth)
|
|
||||||
|
|
||||||
opNames := make(nameSet)
|
|
||||||
fragUsedBy := make(map[*types.FragmentDefinition][]*types.OperationDefinition)
|
|
||||||
for _, op := range doc.Operations {
|
|
||||||
c.usedVars[op] = make(varSet)
|
|
||||||
opc := &opContext{c, []*types.OperationDefinition{op}}
|
|
||||||
|
|
||||||
// Check if max depth is exceeded, if it's set. If max depth is exceeded,
|
|
||||||
// don't continue to validate the document and exit early.
|
|
||||||
if validateMaxDepth(opc, op.Selections, nil, 1) {
|
|
||||||
return c.errs
|
|
||||||
}
|
|
||||||
|
|
||||||
if op.Name.Name == "" && len(doc.Operations) != 1 {
|
|
||||||
c.addErr(op.Loc, "LoneAnonymousOperation", "This anonymous operation must be the only defined operation.")
|
|
||||||
}
|
|
||||||
if op.Name.Name != "" {
|
|
||||||
validateName(c, opNames, op.Name, "UniqueOperationNames", "operation")
|
|
||||||
}
|
|
||||||
|
|
||||||
validateDirectives(opc, string(op.Type), op.Directives)
|
|
||||||
|
|
||||||
varNames := make(nameSet)
|
|
||||||
for _, v := range op.Vars {
|
|
||||||
validateName(c, varNames, v.Name, "UniqueVariableNames", "variable")
|
|
||||||
|
|
||||||
t := resolveType(c, v.Type)
|
|
||||||
if !canBeInput(t) {
|
|
||||||
c.addErr(v.TypeLoc, "VariablesAreInputTypes", "Variable %q cannot be non-input type %q.", "$"+v.Name.Name, t)
|
|
||||||
}
|
|
||||||
validateValue(opc, v, variables[v.Name.Name], t)
|
|
||||||
|
|
||||||
if v.Default != nil {
|
|
||||||
validateLiteral(opc, v.Default)
|
|
||||||
|
|
||||||
if t != nil {
|
|
||||||
if nn, ok := t.(*types.NonNull); ok {
|
|
||||||
c.addErr(v.Default.Location(), "DefaultValuesOfCorrectType", "Variable %q of type %q is required and will not use the default value. Perhaps you meant to use type %q.", "$"+v.Name.Name, t, nn.OfType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok, reason := validateValueType(opc, v.Default, t); !ok {
|
|
||||||
c.addErr(v.Default.Location(), "DefaultValuesOfCorrectType", "Variable %q of type %q has invalid default value %s.\n%s", "$"+v.Name.Name, t, v.Default, reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var entryPoint types.NamedType
|
|
||||||
switch op.Type {
|
|
||||||
case query.Query:
|
|
||||||
entryPoint = s.EntryPoints["query"]
|
|
||||||
case query.Mutation:
|
|
||||||
entryPoint = s.EntryPoints["mutation"]
|
|
||||||
case query.Subscription:
|
|
||||||
entryPoint = s.EntryPoints["subscription"]
|
|
||||||
default:
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
validateSelectionSet(opc, op.Selections, entryPoint)
|
|
||||||
|
|
||||||
fragUsed := make(map[*types.FragmentDefinition]struct{})
|
|
||||||
markUsedFragments(c, op.Selections, fragUsed)
|
|
||||||
for frag := range fragUsed {
|
|
||||||
fragUsedBy[frag] = append(fragUsedBy[frag], op)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fragNames := make(nameSet)
|
|
||||||
fragVisited := make(map[*types.FragmentDefinition]struct{})
|
|
||||||
for _, frag := range doc.Fragments {
|
|
||||||
opc := &opContext{c, fragUsedBy[frag]}
|
|
||||||
|
|
||||||
validateName(c, fragNames, frag.Name, "UniqueFragmentNames", "fragment")
|
|
||||||
validateDirectives(opc, "FRAGMENT_DEFINITION", frag.Directives)
|
|
||||||
|
|
||||||
t := unwrapType(resolveType(c, &frag.On))
|
|
||||||
// continue even if t is nil
|
|
||||||
if t != nil && !canBeFragment(t) {
|
|
||||||
c.addErr(frag.On.Loc, "FragmentsOnCompositeTypes", "Fragment %q cannot condition on non composite type %q.", frag.Name.Name, t)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
validateSelectionSet(opc, frag.Selections, t)
|
|
||||||
|
|
||||||
if _, ok := fragVisited[frag]; !ok {
|
|
||||||
detectFragmentCycle(c, frag.Selections, fragVisited, nil, map[string]int{frag.Name.Name: 0})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, frag := range doc.Fragments {
|
|
||||||
if len(fragUsedBy[frag]) == 0 {
|
|
||||||
c.addErr(frag.Loc, "NoUnusedFragments", "Fragment %q is never used.", frag.Name.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, op := range doc.Operations {
|
|
||||||
c.errs = append(c.errs, c.opErrs[op]...)
|
|
||||||
|
|
||||||
opUsedVars := c.usedVars[op]
|
|
||||||
for _, v := range op.Vars {
|
|
||||||
if _, ok := opUsedVars[v]; !ok {
|
|
||||||
opSuffix := ""
|
|
||||||
if op.Name.Name != "" {
|
|
||||||
opSuffix = fmt.Sprintf(" in operation %q", op.Name.Name)
|
|
||||||
}
|
|
||||||
c.addErr(v.Loc, "NoUnusedVariables", "Variable %q is never used%s.", "$"+v.Name.Name, opSuffix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.errs
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateValue(c *opContext, v *types.InputValueDefinition, val interface{}, t types.Type) {
|
|
||||||
switch t := t.(type) {
|
|
||||||
case *types.NonNull:
|
|
||||||
if val == nil {
|
|
||||||
c.addErr(v.Loc, "VariablesOfCorrectType", "Variable \"%s\" has invalid value null.\nExpected type \"%s\", found null.", v.Name.Name, t)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
validateValue(c, v, val, t.OfType)
|
|
||||||
case *types.List:
|
|
||||||
if val == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vv, ok := val.([]interface{})
|
|
||||||
if !ok {
|
|
||||||
// Input coercion rules allow single items without wrapping array
|
|
||||||
validateValue(c, v, val, t.OfType)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, elem := range vv {
|
|
||||||
validateValue(c, v, elem, t.OfType)
|
|
||||||
}
|
|
||||||
case *types.EnumTypeDefinition:
|
|
||||||
if val == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
e, ok := val.(string)
|
|
||||||
if !ok {
|
|
||||||
c.addErr(v.Loc, "VariablesOfCorrectType", "Variable \"%s\" has invalid type %T.\nExpected type \"%s\", found %v.", v.Name.Name, val, t, val)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, option := range t.EnumValuesDefinition {
|
|
||||||
if option.EnumValue == e {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.addErr(v.Loc, "VariablesOfCorrectType", "Variable \"%s\" has invalid value %s.\nExpected type \"%s\", found %s.", v.Name.Name, e, t, e)
|
|
||||||
case *types.InputObject:
|
|
||||||
if val == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
in, ok := val.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
c.addErr(v.Loc, "VariablesOfCorrectType", "Variable \"%s\" has invalid type %T.\nExpected type \"%s\", found %s.", v.Name.Name, val, t, val)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, f := range t.Values {
|
|
||||||
fieldVal := in[f.Name.Name]
|
|
||||||
validateValue(c, f, fieldVal, f.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// validates the query doesn't go deeper than maxDepth (if set). Returns whether
|
|
||||||
// or not query validated max depth to avoid excessive recursion.
|
|
||||||
//
|
|
||||||
// The visited map is necessary to ensure that max depth validation does not get stuck in cyclical
|
|
||||||
// fragment spreads.
|
|
||||||
func validateMaxDepth(c *opContext, sels []types.Selection, visited map[*types.FragmentDefinition]struct{}, depth int) bool {
|
|
||||||
// maxDepth checking is turned off when maxDepth is 0
|
|
||||||
if c.maxDepth == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
exceededMaxDepth := false
|
|
||||||
if visited == nil {
|
|
||||||
visited = map[*types.FragmentDefinition]struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, sel := range sels {
|
|
||||||
switch sel := sel.(type) {
|
|
||||||
case *types.Field:
|
|
||||||
if depth > c.maxDepth {
|
|
||||||
exceededMaxDepth = true
|
|
||||||
c.addErr(sel.Alias.Loc, "MaxDepthExceeded", "Field %q has depth %d that exceeds max depth %d", sel.Name.Name, depth, c.maxDepth)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
exceededMaxDepth = exceededMaxDepth || validateMaxDepth(c, sel.SelectionSet, visited, depth+1)
|
|
||||||
|
|
||||||
case *types.InlineFragment:
|
|
||||||
// Depth is not checked because inline fragments resolve to other fields which are checked.
|
|
||||||
// Depth is not incremented because inline fragments have the same depth as neighboring fields
|
|
||||||
exceededMaxDepth = exceededMaxDepth || validateMaxDepth(c, sel.Selections, visited, depth)
|
|
||||||
case *types.FragmentSpread:
|
|
||||||
// Depth is not checked because fragments resolve to other fields which are checked.
|
|
||||||
frag := c.doc.Fragments.Get(sel.Name.Name)
|
|
||||||
if frag == nil {
|
|
||||||
// In case of unknown fragment (invalid request), ignore max depth evaluation
|
|
||||||
c.addErr(sel.Loc, "MaxDepthEvaluationError", "Unknown fragment %q. Unable to evaluate depth.", sel.Name.Name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := visited[frag]; ok {
|
|
||||||
// we've already seen this fragment, don't check depth again.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
visited[frag] = struct{}{}
|
|
||||||
|
|
||||||
// Depth is not incremented because fragments have the same depth as surrounding fields
|
|
||||||
exceededMaxDepth = exceededMaxDepth || validateMaxDepth(c, frag.Selections, visited, depth)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return exceededMaxDepth
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateSelectionSet(c *opContext, sels []types.Selection, t types.NamedType) {
|
|
||||||
for _, sel := range sels {
|
|
||||||
validateSelection(c, sel, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, a := range sels {
|
|
||||||
for _, b := range sels[i+1:] {
|
|
||||||
c.validateOverlap(a, b, nil, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateSelection(c *opContext, sel types.Selection, t types.NamedType) {
|
|
||||||
switch sel := sel.(type) {
|
|
||||||
case *types.Field:
|
|
||||||
validateDirectives(c, "FIELD", sel.Directives)
|
|
||||||
|
|
||||||
fieldName := sel.Name.Name
|
|
||||||
var f *types.FieldDefinition
|
|
||||||
switch fieldName {
|
|
||||||
case "__typename":
|
|
||||||
f = &types.FieldDefinition{
|
|
||||||
Name: "__typename",
|
|
||||||
Type: c.schema.Types["String"],
|
|
||||||
}
|
|
||||||
case "__schema":
|
|
||||||
f = &types.FieldDefinition{
|
|
||||||
Name: "__schema",
|
|
||||||
Type: c.schema.Types["__Schema"],
|
|
||||||
}
|
|
||||||
case "__type":
|
|
||||||
f = &types.FieldDefinition{
|
|
||||||
Name: "__type",
|
|
||||||
Arguments: types.ArgumentsDefinition{
|
|
||||||
&types.InputValueDefinition{
|
|
||||||
Name: types.Ident{Name: "name"},
|
|
||||||
Type: &types.NonNull{OfType: c.schema.Types["String"]},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Type: c.schema.Types["__Type"],
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
f = fields(t).Get(fieldName)
|
|
||||||
if f == nil && t != nil {
|
|
||||||
suggestion := makeSuggestion("Did you mean", fields(t).Names(), fieldName)
|
|
||||||
c.addErr(sel.Alias.Loc, "FieldsOnCorrectType", "Cannot query field %q on type %q.%s", fieldName, t, suggestion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.fieldMap[sel] = fieldInfo{sf: f, parent: t}
|
|
||||||
|
|
||||||
validateArgumentLiterals(c, sel.Arguments)
|
|
||||||
if f != nil {
|
|
||||||
validateArgumentTypes(c, sel.Arguments, f.Arguments, sel.Alias.Loc,
|
|
||||||
func() string { return fmt.Sprintf("field %q of type %q", fieldName, t) },
|
|
||||||
func() string { return fmt.Sprintf("Field %q", fieldName) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var ft types.Type
|
|
||||||
if f != nil {
|
|
||||||
ft = f.Type
|
|
||||||
sf := hasSubfields(ft)
|
|
||||||
if sf && sel.SelectionSet == nil {
|
|
||||||
c.addErr(sel.Alias.Loc, "ScalarLeafs", "Field %q of type %q must have a selection of subfields. Did you mean \"%s { ... }\"?", fieldName, ft, fieldName)
|
|
||||||
}
|
|
||||||
if !sf && sel.SelectionSet != nil {
|
|
||||||
c.addErr(sel.SelectionSetLoc, "ScalarLeafs", "Field %q must not have a selection since type %q has no subfields.", fieldName, ft)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if sel.SelectionSet != nil {
|
|
||||||
validateSelectionSet(c, sel.SelectionSet, unwrapType(ft))
|
|
||||||
}
|
|
||||||
|
|
||||||
case *types.InlineFragment:
|
|
||||||
validateDirectives(c, "INLINE_FRAGMENT", sel.Directives)
|
|
||||||
if sel.On.Name != "" {
|
|
||||||
fragTyp := unwrapType(resolveType(c.context, &sel.On))
|
|
||||||
if fragTyp != nil && !compatible(t, fragTyp) {
|
|
||||||
c.addErr(sel.Loc, "PossibleFragmentSpreads", "Fragment cannot be spread here as objects of type %q can never be of type %q.", t, fragTyp)
|
|
||||||
}
|
|
||||||
t = fragTyp
|
|
||||||
// continue even if t is nil
|
|
||||||
}
|
|
||||||
if t != nil && !canBeFragment(t) {
|
|
||||||
c.addErr(sel.On.Loc, "FragmentsOnCompositeTypes", "Fragment cannot condition on non composite type %q.", t)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
validateSelectionSet(c, sel.Selections, unwrapType(t))
|
|
||||||
|
|
||||||
case *types.FragmentSpread:
|
|
||||||
validateDirectives(c, "FRAGMENT_SPREAD", sel.Directives)
|
|
||||||
frag := c.doc.Fragments.Get(sel.Name.Name)
|
|
||||||
if frag == nil {
|
|
||||||
c.addErr(sel.Name.Loc, "KnownFragmentNames", "Unknown fragment %q.", sel.Name.Name)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fragTyp := c.schema.Types[frag.On.Name]
|
|
||||||
if !compatible(t, fragTyp) {
|
|
||||||
c.addErr(sel.Loc, "PossibleFragmentSpreads", "Fragment %q cannot be spread here as objects of type %q can never be of type %q.", frag.Name.Name, t, fragTyp)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func compatible(a, b types.Type) bool {
|
|
||||||
for _, pta := range possibleTypes(a) {
|
|
||||||
for _, ptb := range possibleTypes(b) {
|
|
||||||
if pta == ptb {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func possibleTypes(t types.Type) []*types.ObjectTypeDefinition {
|
|
||||||
switch t := t.(type) {
|
|
||||||
case *types.ObjectTypeDefinition:
|
|
||||||
return []*types.ObjectTypeDefinition{t}
|
|
||||||
case *types.InterfaceTypeDefinition:
|
|
||||||
return t.PossibleTypes
|
|
||||||
case *types.Union:
|
|
||||||
return t.UnionMemberTypes
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func markUsedFragments(c *context, sels []types.Selection, fragUsed map[*types.FragmentDefinition]struct{}) {
|
|
||||||
for _, sel := range sels {
|
|
||||||
switch sel := sel.(type) {
|
|
||||||
case *types.Field:
|
|
||||||
if sel.SelectionSet != nil {
|
|
||||||
markUsedFragments(c, sel.SelectionSet, fragUsed)
|
|
||||||
}
|
|
||||||
|
|
||||||
case *types.InlineFragment:
|
|
||||||
markUsedFragments(c, sel.Selections, fragUsed)
|
|
||||||
|
|
||||||
case *types.FragmentSpread:
|
|
||||||
frag := c.doc.Fragments.Get(sel.Name.Name)
|
|
||||||
if frag == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := fragUsed[frag]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
fragUsed[frag] = struct{}{}
|
|
||||||
markUsedFragments(c, frag.Selections, fragUsed)
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func detectFragmentCycle(c *context, sels []types.Selection, fragVisited map[*types.FragmentDefinition]struct{}, spreadPath []*types.FragmentSpread, spreadPathIndex map[string]int) {
|
|
||||||
for _, sel := range sels {
|
|
||||||
detectFragmentCycleSel(c, sel, fragVisited, spreadPath, spreadPathIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func detectFragmentCycleSel(c *context, sel types.Selection, fragVisited map[*types.FragmentDefinition]struct{}, spreadPath []*types.FragmentSpread, spreadPathIndex map[string]int) {
|
|
||||||
switch sel := sel.(type) {
|
|
||||||
case *types.Field:
|
|
||||||
if sel.SelectionSet != nil {
|
|
||||||
detectFragmentCycle(c, sel.SelectionSet, fragVisited, spreadPath, spreadPathIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
case *types.InlineFragment:
|
|
||||||
detectFragmentCycle(c, sel.Selections, fragVisited, spreadPath, spreadPathIndex)
|
|
||||||
|
|
||||||
case *types.FragmentSpread:
|
|
||||||
frag := c.doc.Fragments.Get(sel.Name.Name)
|
|
||||||
if frag == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
spreadPath = append(spreadPath, sel)
|
|
||||||
if i, ok := spreadPathIndex[frag.Name.Name]; ok {
|
|
||||||
cyclePath := spreadPath[i:]
|
|
||||||
via := ""
|
|
||||||
if len(cyclePath) > 1 {
|
|
||||||
names := make([]string, len(cyclePath)-1)
|
|
||||||
for i, frag := range cyclePath[:len(cyclePath)-1] {
|
|
||||||
names[i] = frag.Name.Name
|
|
||||||
}
|
|
||||||
via = " via " + strings.Join(names, ", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
locs := make([]errors.Location, len(cyclePath))
|
|
||||||
for i, frag := range cyclePath {
|
|
||||||
locs[i] = frag.Loc
|
|
||||||
}
|
|
||||||
c.addErrMultiLoc(locs, "NoFragmentCycles", "Cannot spread fragment %q within itself%s.", frag.Name.Name, via)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := fragVisited[frag]; ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fragVisited[frag] = struct{}{}
|
|
||||||
|
|
||||||
spreadPathIndex[frag.Name.Name] = len(spreadPath)
|
|
||||||
detectFragmentCycle(c, frag.Selections, fragVisited, spreadPath, spreadPathIndex)
|
|
||||||
delete(spreadPathIndex, frag.Name.Name)
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *context) validateOverlap(a, b types.Selection, reasons *[]string, locs *[]errors.Location) {
|
|
||||||
if a == b {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := c.overlapValidated[selectionPair{a, b}]; ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.overlapValidated[selectionPair{a, b}] = struct{}{}
|
|
||||||
c.overlapValidated[selectionPair{b, a}] = struct{}{}
|
|
||||||
|
|
||||||
switch a := a.(type) {
|
|
||||||
case *types.Field:
|
|
||||||
switch b := b.(type) {
|
|
||||||
case *types.Field:
|
|
||||||
if b.Alias.Loc.Before(a.Alias.Loc) {
|
|
||||||
a, b = b, a
|
|
||||||
}
|
|
||||||
if reasons2, locs2 := c.validateFieldOverlap(a, b); len(reasons2) != 0 {
|
|
||||||
locs2 = append(locs2, a.Alias.Loc, b.Alias.Loc)
|
|
||||||
if reasons == nil {
|
|
||||||
c.addErrMultiLoc(locs2, "OverlappingFieldsCanBeMerged", "Fields %q conflict because %s. Use different aliases on the fields to fetch both if this was intentional.", a.Alias.Name, strings.Join(reasons2, " and "))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, r := range reasons2 {
|
|
||||||
*reasons = append(*reasons, fmt.Sprintf("subfields %q conflict because %s", a.Alias.Name, r))
|
|
||||||
}
|
|
||||||
*locs = append(*locs, locs2...)
|
|
||||||
}
|
|
||||||
|
|
||||||
case *types.InlineFragment:
|
|
||||||
for _, sel := range b.Selections {
|
|
||||||
c.validateOverlap(a, sel, reasons, locs)
|
|
||||||
}
|
|
||||||
|
|
||||||
case *types.FragmentSpread:
|
|
||||||
if frag := c.doc.Fragments.Get(b.Name.Name); frag != nil {
|
|
||||||
for _, sel := range frag.Selections {
|
|
||||||
c.validateOverlap(a, sel, reasons, locs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
case *types.InlineFragment:
|
|
||||||
for _, sel := range a.Selections {
|
|
||||||
c.validateOverlap(sel, b, reasons, locs)
|
|
||||||
}
|
|
||||||
|
|
||||||
case *types.FragmentSpread:
|
|
||||||
if frag := c.doc.Fragments.Get(a.Name.Name); frag != nil {
|
|
||||||
for _, sel := range frag.Selections {
|
|
||||||
c.validateOverlap(sel, b, reasons, locs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *context) validateFieldOverlap(a, b *types.Field) ([]string, []errors.Location) {
|
|
||||||
if a.Alias.Name != b.Alias.Name {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if asf := c.fieldMap[a].sf; asf != nil {
|
|
||||||
if bsf := c.fieldMap[b].sf; bsf != nil {
|
|
||||||
if !typesCompatible(asf.Type, bsf.Type) {
|
|
||||||
return []string{fmt.Sprintf("they return conflicting types %s and %s", asf.Type, bsf.Type)}, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
at := c.fieldMap[a].parent
|
|
||||||
bt := c.fieldMap[b].parent
|
|
||||||
if at == nil || bt == nil || at == bt {
|
|
||||||
if a.Name.Name != b.Name.Name {
|
|
||||||
return []string{fmt.Sprintf("%s and %s are different fields", a.Name.Name, b.Name.Name)}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if argumentsConflict(a.Arguments, b.Arguments) {
|
|
||||||
return []string{"they have differing arguments"}, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var reasons []string
|
|
||||||
var locs []errors.Location
|
|
||||||
for _, a2 := range a.SelectionSet {
|
|
||||||
for _, b2 := range b.SelectionSet {
|
|
||||||
c.validateOverlap(a2, b2, &reasons, &locs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return reasons, locs
|
|
||||||
}
|
|
||||||
|
|
||||||
func argumentsConflict(a, b types.ArgumentList) bool {
|
|
||||||
if len(a) != len(b) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for _, argA := range a {
|
|
||||||
valB, ok := b.Get(argA.Name.Name)
|
|
||||||
if !ok || !reflect.DeepEqual(argA.Value.Deserialize(nil), valB.Deserialize(nil)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func fields(t types.Type) types.FieldsDefinition {
|
|
||||||
switch t := t.(type) {
|
|
||||||
case *types.ObjectTypeDefinition:
|
|
||||||
return t.Fields
|
|
||||||
case *types.InterfaceTypeDefinition:
|
|
||||||
return t.Fields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func unwrapType(t types.Type) types.NamedType {
|
|
||||||
if t == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
switch t2 := t.(type) {
|
|
||||||
case types.NamedType:
|
|
||||||
return t2
|
|
||||||
case *types.List:
|
|
||||||
t = t2.OfType
|
|
||||||
case *types.NonNull:
|
|
||||||
t = t2.OfType
|
|
||||||
default:
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveType(c *context, t types.Type) types.Type {
|
|
||||||
t2, err := common.ResolveType(t, c.schema.Resolve)
|
|
||||||
if err != nil {
|
|
||||||
c.errs = append(c.errs, err)
|
|
||||||
}
|
|
||||||
return t2
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateDirectives(c *opContext, loc string, directives types.DirectiveList) {
|
|
||||||
directiveNames := make(nameSet)
|
|
||||||
for _, d := range directives {
|
|
||||||
dirName := d.Name.Name
|
|
||||||
validateNameCustomMsg(c.context, directiveNames, d.Name, "UniqueDirectivesPerLocation", func() string {
|
|
||||||
return fmt.Sprintf("The directive %q can only be used once at this location.", dirName)
|
|
||||||
})
|
|
||||||
|
|
||||||
validateArgumentLiterals(c, d.Arguments)
|
|
||||||
|
|
||||||
dd, ok := c.schema.Directives[dirName]
|
|
||||||
if !ok {
|
|
||||||
c.addErr(d.Name.Loc, "KnownDirectives", "Unknown directive %q.", dirName)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
locOK := false
|
|
||||||
for _, allowedLoc := range dd.Locations {
|
|
||||||
if loc == allowedLoc {
|
|
||||||
locOK = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !locOK {
|
|
||||||
c.addErr(d.Name.Loc, "KnownDirectives", "Directive %q may not be used on %s.", dirName, loc)
|
|
||||||
}
|
|
||||||
|
|
||||||
validateArgumentTypes(c, d.Arguments, dd.Arguments, d.Name.Loc,
|
|
||||||
func() string { return fmt.Sprintf("directive %q", "@"+dirName) },
|
|
||||||
func() string { return fmt.Sprintf("Directive %q", "@"+dirName) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateName(c *context, set nameSet, name types.Ident, rule string, kind string) {
|
|
||||||
validateNameCustomMsg(c, set, name, rule, func() string {
|
|
||||||
return fmt.Sprintf("There can be only one %s named %q.", kind, name.Name)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateNameCustomMsg(c *context, set nameSet, name types.Ident, rule string, msg func() string) {
|
|
||||||
if loc, ok := set[name.Name]; ok {
|
|
||||||
c.addErrMultiLoc([]errors.Location{loc, name.Loc}, rule, msg())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
set[name.Name] = name.Loc
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateArgumentTypes(c *opContext, args types.ArgumentList, argDecls types.ArgumentsDefinition, loc errors.Location, owner1, owner2 func() string) {
|
|
||||||
for _, selArg := range args {
|
|
||||||
arg := argDecls.Get(selArg.Name.Name)
|
|
||||||
if arg == nil {
|
|
||||||
c.addErr(selArg.Name.Loc, "KnownArgumentNames", "Unknown argument %q on %s.", selArg.Name.Name, owner1())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
value := selArg.Value
|
|
||||||
if ok, reason := validateValueType(c, value, arg.Type); !ok {
|
|
||||||
c.addErr(value.Location(), "ArgumentsOfCorrectType", "Argument %q has invalid value %s.\n%s", arg.Name.Name, value, reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, decl := range argDecls {
|
|
||||||
if _, ok := decl.Type.(*types.NonNull); ok {
|
|
||||||
if _, ok := args.Get(decl.Name.Name); !ok {
|
|
||||||
c.addErr(loc, "ProvidedNonNullArguments", "%s argument %q of type %q is required but not provided.", owner2(), decl.Name.Name, decl.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateArgumentLiterals(c *opContext, args types.ArgumentList) {
|
|
||||||
argNames := make(nameSet)
|
|
||||||
for _, arg := range args {
|
|
||||||
validateName(c.context, argNames, arg.Name, "UniqueArgumentNames", "argument")
|
|
||||||
validateLiteral(c, arg.Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateLiteral(c *opContext, l types.Value) {
|
|
||||||
switch l := l.(type) {
|
|
||||||
case *types.ObjectValue:
|
|
||||||
fieldNames := make(nameSet)
|
|
||||||
for _, f := range l.Fields {
|
|
||||||
validateName(c.context, fieldNames, f.Name, "UniqueInputFieldNames", "input field")
|
|
||||||
validateLiteral(c, f.Value)
|
|
||||||
}
|
|
||||||
case *types.ListValue:
|
|
||||||
for _, entry := range l.Values {
|
|
||||||
validateLiteral(c, entry)
|
|
||||||
}
|
|
||||||
case *types.Variable:
|
|
||||||
for _, op := range c.ops {
|
|
||||||
v := op.Vars.Get(l.Name)
|
|
||||||
if v == nil {
|
|
||||||
byOp := ""
|
|
||||||
if op.Name.Name != "" {
|
|
||||||
byOp = fmt.Sprintf(" by operation %q", op.Name.Name)
|
|
||||||
}
|
|
||||||
c.opErrs[op] = append(c.opErrs[op], &errors.QueryError{
|
|
||||||
Message: fmt.Sprintf("Variable %q is not defined%s.", "$"+l.Name, byOp),
|
|
||||||
Locations: []errors.Location{l.Loc, op.Loc},
|
|
||||||
Rule: "NoUndefinedVariables",
|
|
||||||
})
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
validateValueType(c, l, resolveType(c.context, v.Type))
|
|
||||||
c.usedVars[op][v] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateValueType(c *opContext, v types.Value, t types.Type) (bool, string) {
|
|
||||||
if v, ok := v.(*types.Variable); ok {
|
|
||||||
for _, op := range c.ops {
|
|
||||||
if v2 := op.Vars.Get(v.Name); v2 != nil {
|
|
||||||
t2, err := common.ResolveType(v2.Type, c.schema.Resolve)
|
|
||||||
if _, ok := t2.(*types.NonNull); !ok && v2.Default != nil {
|
|
||||||
t2 = &types.NonNull{OfType: t2}
|
|
||||||
}
|
|
||||||
if err == nil && !typeCanBeUsedAs(t2, t) {
|
|
||||||
c.addErrMultiLoc([]errors.Location{v2.Loc, v.Loc}, "VariablesInAllowedPosition", "Variable %q of type %q used in position expecting type %q.", "$"+v.Name, t2, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if nn, ok := t.(*types.NonNull); ok {
|
|
||||||
if isNull(v) {
|
|
||||||
return false, fmt.Sprintf("Expected %q, found null.", t)
|
|
||||||
}
|
|
||||||
t = nn.OfType
|
|
||||||
}
|
|
||||||
if isNull(v) {
|
|
||||||
return true, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
switch t := t.(type) {
|
|
||||||
case *types.ScalarTypeDefinition, *types.EnumTypeDefinition:
|
|
||||||
if lit, ok := v.(*types.PrimitiveValue); ok {
|
|
||||||
if validateBasicLit(lit, t) {
|
|
||||||
return true, ""
|
|
||||||
}
|
|
||||||
return false, fmt.Sprintf("Expected type %q, found %s.", t, v)
|
|
||||||
}
|
|
||||||
return true, ""
|
|
||||||
|
|
||||||
case *types.List:
|
|
||||||
list, ok := v.(*types.ListValue)
|
|
||||||
if !ok {
|
|
||||||
return validateValueType(c, v, t.OfType) // single value instead of list
|
|
||||||
}
|
|
||||||
for i, entry := range list.Values {
|
|
||||||
if ok, reason := validateValueType(c, entry, t.OfType); !ok {
|
|
||||||
return false, fmt.Sprintf("In element #%d: %s", i, reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true, ""
|
|
||||||
|
|
||||||
case *types.InputObject:
|
|
||||||
v, ok := v.(*types.ObjectValue)
|
|
||||||
if !ok {
|
|
||||||
return false, fmt.Sprintf("Expected %q, found not an object.", t)
|
|
||||||
}
|
|
||||||
for _, f := range v.Fields {
|
|
||||||
name := f.Name.Name
|
|
||||||
iv := t.Values.Get(name)
|
|
||||||
if iv == nil {
|
|
||||||
return false, fmt.Sprintf("In field %q: Unknown field.", name)
|
|
||||||
}
|
|
||||||
if ok, reason := validateValueType(c, f.Value, iv.Type); !ok {
|
|
||||||
return false, fmt.Sprintf("In field %q: %s", name, reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, iv := range t.Values {
|
|
||||||
found := false
|
|
||||||
for _, f := range v.Fields {
|
|
||||||
if f.Name.Name == iv.Name.Name {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
if _, ok := iv.Type.(*types.NonNull); ok && iv.Default == nil {
|
|
||||||
return false, fmt.Sprintf("In field %q: Expected %q, found null.", iv.Name.Name, iv.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, fmt.Sprintf("Expected type %q, found %s.", t, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateBasicLit(v *types.PrimitiveValue, t types.Type) bool {
|
|
||||||
switch t := t.(type) {
|
|
||||||
case *types.ScalarTypeDefinition:
|
|
||||||
switch t.Name {
|
|
||||||
case "Int":
|
|
||||||
if v.Type != scanner.Int {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
f, err := strconv.ParseFloat(v.Text, 64)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return f >= math.MinInt32 && f <= math.MaxInt32
|
|
||||||
case "Float":
|
|
||||||
return v.Type == scanner.Int || v.Type == scanner.Float
|
|
||||||
case "String":
|
|
||||||
return v.Type == scanner.String
|
|
||||||
case "Boolean":
|
|
||||||
return v.Type == scanner.Ident && (v.Text == "true" || v.Text == "false")
|
|
||||||
case "ID":
|
|
||||||
return v.Type == scanner.Int || v.Type == scanner.String
|
|
||||||
default:
|
|
||||||
//TODO: Type-check against expected type by Unmarshalling
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
case *types.EnumTypeDefinition:
|
|
||||||
if v.Type != scanner.Ident {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, option := range t.EnumValuesDefinition {
|
|
||||||
if option.EnumValue == v.Text {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func canBeFragment(t types.Type) bool {
|
|
||||||
switch t.(type) {
|
|
||||||
case *types.ObjectTypeDefinition, *types.InterfaceTypeDefinition, *types.Union:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func canBeInput(t types.Type) bool {
|
|
||||||
switch t := t.(type) {
|
|
||||||
case *types.InputObject, *types.ScalarTypeDefinition, *types.EnumTypeDefinition:
|
|
||||||
return true
|
|
||||||
case *types.List:
|
|
||||||
return canBeInput(t.OfType)
|
|
||||||
case *types.NonNull:
|
|
||||||
return canBeInput(t.OfType)
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasSubfields(t types.Type) bool {
|
|
||||||
switch t := t.(type) {
|
|
||||||
case *types.ObjectTypeDefinition, *types.InterfaceTypeDefinition, *types.Union:
|
|
||||||
return true
|
|
||||||
case *types.List:
|
|
||||||
return hasSubfields(t.OfType)
|
|
||||||
case *types.NonNull:
|
|
||||||
return hasSubfields(t.OfType)
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isLeaf(t types.Type) bool {
|
|
||||||
switch t.(type) {
|
|
||||||
case *types.ScalarTypeDefinition, *types.EnumTypeDefinition:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNull(lit interface{}) bool {
|
|
||||||
_, ok := lit.(*types.NullValue)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func typesCompatible(a, b types.Type) bool {
|
|
||||||
al, aIsList := a.(*types.List)
|
|
||||||
bl, bIsList := b.(*types.List)
|
|
||||||
if aIsList || bIsList {
|
|
||||||
return aIsList && bIsList && typesCompatible(al.OfType, bl.OfType)
|
|
||||||
}
|
|
||||||
|
|
||||||
ann, aIsNN := a.(*types.NonNull)
|
|
||||||
bnn, bIsNN := b.(*types.NonNull)
|
|
||||||
if aIsNN || bIsNN {
|
|
||||||
return aIsNN && bIsNN && typesCompatible(ann.OfType, bnn.OfType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if isLeaf(a) || isLeaf(b) {
|
|
||||||
return a == b
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func typeCanBeUsedAs(t, as types.Type) bool {
|
|
||||||
nnT, okT := t.(*types.NonNull)
|
|
||||||
if okT {
|
|
||||||
t = nnT.OfType
|
|
||||||
}
|
|
||||||
|
|
||||||
nnAs, okAs := as.(*types.NonNull)
|
|
||||||
if okAs {
|
|
||||||
as = nnAs.OfType
|
|
||||||
if !okT {
|
|
||||||
return false // nullable can not be used as non-null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if t == as {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if lT, ok := t.(*types.List); ok {
|
|
||||||
if lAs, ok := as.(*types.List); ok {
|
|
||||||
return typeCanBeUsedAs(lT.OfType, lAs.OfType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
118
vendor/github.com/graph-gophers/graphql-go/introspection.go
generated
vendored
118
vendor/github.com/graph-gophers/graphql-go/introspection.go
generated
vendored
@ -1,118 +0,0 @@
|
|||||||
package graphql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/exec/resolvable"
|
|
||||||
"github.com/graph-gophers/graphql-go/introspection"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Inspect allows inspection of the given schema.
|
|
||||||
func (s *Schema) Inspect() *introspection.Schema {
|
|
||||||
return introspection.WrapSchema(s.schema)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToJSON encodes the schema in a JSON format used by tools like Relay.
|
|
||||||
func (s *Schema) ToJSON() ([]byte, error) {
|
|
||||||
result := s.exec(context.Background(), introspectionQuery, "", nil, &resolvable.Schema{
|
|
||||||
Meta: s.res.Meta,
|
|
||||||
Query: &resolvable.Object{},
|
|
||||||
Schema: *s.schema,
|
|
||||||
})
|
|
||||||
if len(result.Errors) != 0 {
|
|
||||||
panic(result.Errors[0])
|
|
||||||
}
|
|
||||||
return json.MarshalIndent(result.Data, "", "\t")
|
|
||||||
}
|
|
||||||
|
|
||||||
var introspectionQuery = `
|
|
||||||
query {
|
|
||||||
__schema {
|
|
||||||
queryType { name }
|
|
||||||
mutationType { name }
|
|
||||||
subscriptionType { name }
|
|
||||||
types {
|
|
||||||
...FullType
|
|
||||||
}
|
|
||||||
directives {
|
|
||||||
name
|
|
||||||
description
|
|
||||||
locations
|
|
||||||
args {
|
|
||||||
...InputValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fragment FullType on __Type {
|
|
||||||
kind
|
|
||||||
name
|
|
||||||
description
|
|
||||||
fields(includeDeprecated: true) {
|
|
||||||
name
|
|
||||||
description
|
|
||||||
args {
|
|
||||||
...InputValue
|
|
||||||
}
|
|
||||||
type {
|
|
||||||
...TypeRef
|
|
||||||
}
|
|
||||||
isDeprecated
|
|
||||||
deprecationReason
|
|
||||||
}
|
|
||||||
inputFields {
|
|
||||||
...InputValue
|
|
||||||
}
|
|
||||||
interfaces {
|
|
||||||
...TypeRef
|
|
||||||
}
|
|
||||||
enumValues(includeDeprecated: true) {
|
|
||||||
name
|
|
||||||
description
|
|
||||||
isDeprecated
|
|
||||||
deprecationReason
|
|
||||||
}
|
|
||||||
possibleTypes {
|
|
||||||
...TypeRef
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fragment InputValue on __InputValue {
|
|
||||||
name
|
|
||||||
description
|
|
||||||
type { ...TypeRef }
|
|
||||||
defaultValue
|
|
||||||
}
|
|
||||||
fragment TypeRef on __Type {
|
|
||||||
kind
|
|
||||||
name
|
|
||||||
ofType {
|
|
||||||
kind
|
|
||||||
name
|
|
||||||
ofType {
|
|
||||||
kind
|
|
||||||
name
|
|
||||||
ofType {
|
|
||||||
kind
|
|
||||||
name
|
|
||||||
ofType {
|
|
||||||
kind
|
|
||||||
name
|
|
||||||
ofType {
|
|
||||||
kind
|
|
||||||
name
|
|
||||||
ofType {
|
|
||||||
kind
|
|
||||||
name
|
|
||||||
ofType {
|
|
||||||
kind
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
312
vendor/github.com/graph-gophers/graphql-go/introspection/introspection.go
generated
vendored
312
vendor/github.com/graph-gophers/graphql-go/introspection/introspection.go
generated
vendored
@ -1,312 +0,0 @@
|
|||||||
package introspection
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/graph-gophers/graphql-go/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Schema struct {
|
|
||||||
schema *types.Schema
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapSchema is only used internally.
|
|
||||||
func WrapSchema(schema *types.Schema) *Schema {
|
|
||||||
return &Schema{schema}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Schema) Types() []*Type {
|
|
||||||
var names []string
|
|
||||||
for name := range r.schema.Types {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
|
|
||||||
l := make([]*Type, len(names))
|
|
||||||
for i, name := range names {
|
|
||||||
l[i] = &Type{r.schema.Types[name]}
|
|
||||||
}
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Schema) Directives() []*Directive {
|
|
||||||
var names []string
|
|
||||||
for name := range r.schema.Directives {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
|
|
||||||
l := make([]*Directive, len(names))
|
|
||||||
for i, name := range names {
|
|
||||||
l[i] = &Directive{r.schema.Directives[name]}
|
|
||||||
}
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Schema) QueryType() *Type {
|
|
||||||
t, ok := r.schema.EntryPoints["query"]
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &Type{t}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Schema) MutationType() *Type {
|
|
||||||
t, ok := r.schema.EntryPoints["mutation"]
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &Type{t}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Schema) SubscriptionType() *Type {
|
|
||||||
t, ok := r.schema.EntryPoints["subscription"]
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &Type{t}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Type struct {
|
|
||||||
typ types.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapType is only used internally.
|
|
||||||
func WrapType(typ types.Type) *Type {
|
|
||||||
return &Type{typ}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Type) Kind() string {
|
|
||||||
return r.typ.Kind()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Type) Name() *string {
|
|
||||||
if named, ok := r.typ.(types.NamedType); ok {
|
|
||||||
name := named.TypeName()
|
|
||||||
return &name
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Type) Description() *string {
|
|
||||||
if named, ok := r.typ.(types.NamedType); ok {
|
|
||||||
desc := named.Description()
|
|
||||||
if desc == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &desc
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Type) Fields(args *struct{ IncludeDeprecated bool }) *[]*Field {
|
|
||||||
var fields types.FieldsDefinition
|
|
||||||
switch t := r.typ.(type) {
|
|
||||||
case *types.ObjectTypeDefinition:
|
|
||||||
fields = t.Fields
|
|
||||||
case *types.InterfaceTypeDefinition:
|
|
||||||
fields = t.Fields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var l []*Field
|
|
||||||
for _, f := range fields {
|
|
||||||
if d := f.Directives.Get("deprecated"); d == nil || args.IncludeDeprecated {
|
|
||||||
l = append(l, &Field{field: f})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &l
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Type) Interfaces() *[]*Type {
|
|
||||||
t, ok := r.typ.(*types.ObjectTypeDefinition)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
l := make([]*Type, len(t.Interfaces))
|
|
||||||
for i, intf := range t.Interfaces {
|
|
||||||
l[i] = &Type{intf}
|
|
||||||
}
|
|
||||||
return &l
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Type) PossibleTypes() *[]*Type {
|
|
||||||
var possibleTypes []*types.ObjectTypeDefinition
|
|
||||||
switch t := r.typ.(type) {
|
|
||||||
case *types.InterfaceTypeDefinition:
|
|
||||||
possibleTypes = t.PossibleTypes
|
|
||||||
case *types.Union:
|
|
||||||
possibleTypes = t.UnionMemberTypes
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
l := make([]*Type, len(possibleTypes))
|
|
||||||
for i, intf := range possibleTypes {
|
|
||||||
l[i] = &Type{intf}
|
|
||||||
}
|
|
||||||
return &l
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Type) EnumValues(args *struct{ IncludeDeprecated bool }) *[]*EnumValue {
|
|
||||||
t, ok := r.typ.(*types.EnumTypeDefinition)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var l []*EnumValue
|
|
||||||
for _, v := range t.EnumValuesDefinition {
|
|
||||||
if d := v.Directives.Get("deprecated"); d == nil || args.IncludeDeprecated {
|
|
||||||
l = append(l, &EnumValue{v})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &l
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Type) InputFields() *[]*InputValue {
|
|
||||||
t, ok := r.typ.(*types.InputObject)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
l := make([]*InputValue, len(t.Values))
|
|
||||||
for i, v := range t.Values {
|
|
||||||
l[i] = &InputValue{v}
|
|
||||||
}
|
|
||||||
return &l
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Type) OfType() *Type {
|
|
||||||
switch t := r.typ.(type) {
|
|
||||||
case *types.List:
|
|
||||||
return &Type{t.OfType}
|
|
||||||
case *types.NonNull:
|
|
||||||
return &Type{t.OfType}
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Field struct {
|
|
||||||
field *types.FieldDefinition
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Field) Name() string {
|
|
||||||
return r.field.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Field) Description() *string {
|
|
||||||
if r.field.Desc == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &r.field.Desc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Field) Args() []*InputValue {
|
|
||||||
l := make([]*InputValue, len(r.field.Arguments))
|
|
||||||
for i, v := range r.field.Arguments {
|
|
||||||
l[i] = &InputValue{v}
|
|
||||||
}
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Field) Type() *Type {
|
|
||||||
return &Type{r.field.Type}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Field) IsDeprecated() bool {
|
|
||||||
return r.field.Directives.Get("deprecated") != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Field) DeprecationReason() *string {
|
|
||||||
d := r.field.Directives.Get("deprecated")
|
|
||||||
if d == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
reason := d.Arguments.MustGet("reason").Deserialize(nil).(string)
|
|
||||||
return &reason
|
|
||||||
}
|
|
||||||
|
|
||||||
type InputValue struct {
|
|
||||||
value *types.InputValueDefinition
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *InputValue) Name() string {
|
|
||||||
return r.value.Name.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *InputValue) Description() *string {
|
|
||||||
if r.value.Desc == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &r.value.Desc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *InputValue) Type() *Type {
|
|
||||||
return &Type{r.value.Type}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *InputValue) DefaultValue() *string {
|
|
||||||
if r.value.Default == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
s := r.value.Default.String()
|
|
||||||
return &s
|
|
||||||
}
|
|
||||||
|
|
||||||
type EnumValue struct {
|
|
||||||
value *types.EnumValueDefinition
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *EnumValue) Name() string {
|
|
||||||
return r.value.EnumValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *EnumValue) Description() *string {
|
|
||||||
if r.value.Desc == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &r.value.Desc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *EnumValue) IsDeprecated() bool {
|
|
||||||
return r.value.Directives.Get("deprecated") != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *EnumValue) DeprecationReason() *string {
|
|
||||||
d := r.value.Directives.Get("deprecated")
|
|
||||||
if d == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
reason := d.Arguments.MustGet("reason").Deserialize(nil).(string)
|
|
||||||
return &reason
|
|
||||||
}
|
|
||||||
|
|
||||||
type Directive struct {
|
|
||||||
directive *types.DirectiveDefinition
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Directive) Name() string {
|
|
||||||
return r.directive.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Directive) Description() *string {
|
|
||||||
if r.directive.Desc == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &r.directive.Desc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Directive) Locations() []string {
|
|
||||||
return r.directive.Locations
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Directive) Args() []*InputValue {
|
|
||||||
l := make([]*InputValue, len(r.directive.Arguments))
|
|
||||||
for i, v := range r.directive.Arguments {
|
|
||||||
l[i] = &InputValue{v}
|
|
||||||
}
|
|
||||||
return l
|
|
||||||
}
|
|
23
vendor/github.com/graph-gophers/graphql-go/log/log.go
generated
vendored
23
vendor/github.com/graph-gophers/graphql-go/log/log.go
generated
vendored
@ -1,23 +0,0 @@
|
|||||||
package log
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Logger is the interface used to log panics that occur during query execution. It is settable via graphql.ParseSchema
|
|
||||||
type Logger interface {
|
|
||||||
LogPanic(ctx context.Context, value interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultLogger is the default logger used to log panics that occur during query execution
|
|
||||||
type DefaultLogger struct{}
|
|
||||||
|
|
||||||
// LogPanic is used to log recovered panic values that occur during query execution
|
|
||||||
func (l *DefaultLogger) LogPanic(ctx context.Context, value interface{}) {
|
|
||||||
const size = 64 << 10
|
|
||||||
buf := make([]byte, size)
|
|
||||||
buf = buf[:runtime.Stack(buf, false)]
|
|
||||||
log.Printf("graphql: panic occurred: %v\n%s\ncontext: %v", value, buf, ctx)
|
|
||||||
}
|
|
166
vendor/github.com/graph-gophers/graphql-go/nullable_types.go
generated
vendored
166
vendor/github.com/graph-gophers/graphql-go/nullable_types.go
generated
vendored
@ -1,166 +0,0 @@
|
|||||||
package graphql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NullString is a string that can be null. Use it in input structs to
|
|
||||||
// differentiate a value explicitly set to null from an omitted value.
|
|
||||||
// When the value is defined (either null or a value) Set is true.
|
|
||||||
type NullString struct {
|
|
||||||
Value *string
|
|
||||||
Set bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (NullString) ImplementsGraphQLType(name string) bool {
|
|
||||||
return name == "String"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *NullString) UnmarshalGraphQL(input interface{}) error {
|
|
||||||
s.Set = true
|
|
||||||
|
|
||||||
if input == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v := input.(type) {
|
|
||||||
case string:
|
|
||||||
s.Value = &v
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("wrong type for String: %T", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *NullString) Nullable() {}
|
|
||||||
|
|
||||||
// NullBool is a string that can be null. Use it in input structs to
|
|
||||||
// differentiate a value explicitly set to null from an omitted value.
|
|
||||||
// When the value is defined (either null or a value) Set is true.
|
|
||||||
type NullBool struct {
|
|
||||||
Value *bool
|
|
||||||
Set bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (NullBool) ImplementsGraphQLType(name string) bool {
|
|
||||||
return name == "Boolean"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *NullBool) UnmarshalGraphQL(input interface{}) error {
|
|
||||||
s.Set = true
|
|
||||||
|
|
||||||
if input == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v := input.(type) {
|
|
||||||
case bool:
|
|
||||||
s.Value = &v
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("wrong type for Boolean: %T", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *NullBool) Nullable() {}
|
|
||||||
|
|
||||||
// NullInt is a string that can be null. Use it in input structs to
|
|
||||||
// differentiate a value explicitly set to null from an omitted value.
|
|
||||||
// When the value is defined (either null or a value) Set is true.
|
|
||||||
type NullInt struct {
|
|
||||||
Value *int32
|
|
||||||
Set bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (NullInt) ImplementsGraphQLType(name string) bool {
|
|
||||||
return name == "Int"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *NullInt) UnmarshalGraphQL(input interface{}) error {
|
|
||||||
s.Set = true
|
|
||||||
|
|
||||||
if input == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v := input.(type) {
|
|
||||||
case int32:
|
|
||||||
s.Value = &v
|
|
||||||
return nil
|
|
||||||
case float64:
|
|
||||||
coerced := int32(v)
|
|
||||||
if v < math.MinInt32 || v > math.MaxInt32 || float64(coerced) != v {
|
|
||||||
return fmt.Errorf("not a 32-bit integer")
|
|
||||||
}
|
|
||||||
s.Value = &coerced
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("wrong type for Int: %T", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *NullInt) Nullable() {}
|
|
||||||
|
|
||||||
// NullFloat is a string that can be null. Use it in input structs to
|
|
||||||
// differentiate a value explicitly set to null from an omitted value.
|
|
||||||
// When the value is defined (either null or a value) Set is true.
|
|
||||||
type NullFloat struct {
|
|
||||||
Value *float64
|
|
||||||
Set bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (NullFloat) ImplementsGraphQLType(name string) bool {
|
|
||||||
return name == "Float"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *NullFloat) UnmarshalGraphQL(input interface{}) error {
|
|
||||||
s.Set = true
|
|
||||||
|
|
||||||
if input == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v := input.(type) {
|
|
||||||
case float64:
|
|
||||||
s.Value = &v
|
|
||||||
return nil
|
|
||||||
case int32:
|
|
||||||
coerced := float64(v)
|
|
||||||
s.Value = &coerced
|
|
||||||
return nil
|
|
||||||
case int:
|
|
||||||
coerced := float64(v)
|
|
||||||
s.Value = &coerced
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("wrong type for Float: %T", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *NullFloat) Nullable() {}
|
|
||||||
|
|
||||||
// NullTime is a string that can be null. Use it in input structs to
|
|
||||||
// differentiate a value explicitly set to null from an omitted value.
|
|
||||||
// When the value is defined (either null or a value) Set is true.
|
|
||||||
type NullTime struct {
|
|
||||||
Value *Time
|
|
||||||
Set bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (NullTime) ImplementsGraphQLType(name string) bool {
|
|
||||||
return name == "Time"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *NullTime) UnmarshalGraphQL(input interface{}) error {
|
|
||||||
s.Set = true
|
|
||||||
|
|
||||||
if input == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Value = new(Time)
|
|
||||||
return s.Value.UnmarshalGraphQL(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *NullTime) Nullable() {}
|
|
96
vendor/github.com/graph-gophers/graphql-go/subscriptions.go
generated
vendored
96
vendor/github.com/graph-gophers/graphql-go/subscriptions.go
generated
vendored
@ -1,96 +0,0 @@
|
|||||||
package graphql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
qerrors "github.com/graph-gophers/graphql-go/errors"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/common"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/exec"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/exec/resolvable"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/exec/selected"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/query"
|
|
||||||
"github.com/graph-gophers/graphql-go/internal/validation"
|
|
||||||
"github.com/graph-gophers/graphql-go/introspection"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Subscribe returns a response channel for the given subscription with the schema's
|
|
||||||
// resolver. It returns an error if the schema was created without a resolver.
|
|
||||||
// If the context gets cancelled, the response channel will be closed and no
|
|
||||||
// further resolvers will be called. The context error will be returned as soon
|
|
||||||
// as possible (not immediately).
|
|
||||||
func (s *Schema) Subscribe(ctx context.Context, queryString string, operationName string, variables map[string]interface{}) (<-chan interface{}, error) {
|
|
||||||
if !s.res.Resolver.IsValid() {
|
|
||||||
return nil, errors.New("schema created without resolver, can not subscribe")
|
|
||||||
}
|
|
||||||
if _, ok := s.schema.EntryPoints["subscription"]; !ok {
|
|
||||||
return nil, errors.New("no subscriptions are offered by the schema")
|
|
||||||
}
|
|
||||||
return s.subscribe(ctx, queryString, operationName, variables, s.res), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Schema) subscribe(ctx context.Context, queryString string, operationName string, variables map[string]interface{}, res *resolvable.Schema) <-chan interface{} {
|
|
||||||
doc, qErr := query.Parse(queryString)
|
|
||||||
if qErr != nil {
|
|
||||||
return sendAndReturnClosed(&Response{Errors: []*qerrors.QueryError{qErr}})
|
|
||||||
}
|
|
||||||
|
|
||||||
validationFinish := s.validationTracer.TraceValidation(ctx)
|
|
||||||
errs := validation.Validate(s.schema, doc, variables, s.maxDepth)
|
|
||||||
validationFinish(errs)
|
|
||||||
if len(errs) != 0 {
|
|
||||||
return sendAndReturnClosed(&Response{Errors: errs})
|
|
||||||
}
|
|
||||||
|
|
||||||
op, err := getOperation(doc, operationName)
|
|
||||||
if err != nil {
|
|
||||||
return sendAndReturnClosed(&Response{Errors: []*qerrors.QueryError{qerrors.Errorf("%s", err)}})
|
|
||||||
}
|
|
||||||
|
|
||||||
r := &exec.Request{
|
|
||||||
Request: selected.Request{
|
|
||||||
Doc: doc,
|
|
||||||
Vars: variables,
|
|
||||||
Schema: s.schema,
|
|
||||||
},
|
|
||||||
Limiter: make(chan struct{}, s.maxParallelism),
|
|
||||||
Tracer: s.tracer,
|
|
||||||
Logger: s.logger,
|
|
||||||
PanicHandler: s.panicHandler,
|
|
||||||
SubscribeResolverTimeout: s.subscribeResolverTimeout,
|
|
||||||
}
|
|
||||||
varTypes := make(map[string]*introspection.Type)
|
|
||||||
for _, v := range op.Vars {
|
|
||||||
t, err := common.ResolveType(v.Type, s.schema.Resolve)
|
|
||||||
if err != nil {
|
|
||||||
return sendAndReturnClosed(&Response{Errors: []*qerrors.QueryError{err}})
|
|
||||||
}
|
|
||||||
varTypes[v.Name.Name] = introspection.WrapType(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
if op.Type == query.Query || op.Type == query.Mutation {
|
|
||||||
data, errs := r.Execute(ctx, res, op)
|
|
||||||
return sendAndReturnClosed(&Response{Data: data, Errors: errs})
|
|
||||||
}
|
|
||||||
|
|
||||||
responses := r.Subscribe(ctx, res, op)
|
|
||||||
c := make(chan interface{})
|
|
||||||
go func() {
|
|
||||||
for resp := range responses {
|
|
||||||
c <- &Response{
|
|
||||||
Data: resp.Data,
|
|
||||||
Errors: resp.Errors,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close(c)
|
|
||||||
}()
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendAndReturnClosed(resp *Response) chan interface{} {
|
|
||||||
c := make(chan interface{}, 1)
|
|
||||||
c <- resp
|
|
||||||
close(c)
|
|
||||||
return c
|
|
||||||
}
|
|
64
vendor/github.com/graph-gophers/graphql-go/time.go
generated
vendored
64
vendor/github.com/graph-gophers/graphql-go/time.go
generated
vendored
@ -1,64 +0,0 @@
|
|||||||
package graphql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Time is a custom GraphQL type to represent an instant in time. It has to be added to a schema
|
|
||||||
// via "scalar Time" since it is not a predeclared GraphQL type like "ID".
|
|
||||||
type Time struct {
|
|
||||||
time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImplementsGraphQLType maps this custom Go type
|
|
||||||
// to the graphql scalar type in the schema.
|
|
||||||
func (Time) ImplementsGraphQLType(name string) bool {
|
|
||||||
return name == "Time"
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalGraphQL is a custom unmarshaler for Time
|
|
||||||
//
|
|
||||||
// This function will be called whenever you use the
|
|
||||||
// time scalar as an input
|
|
||||||
func (t *Time) UnmarshalGraphQL(input interface{}) error {
|
|
||||||
switch input := input.(type) {
|
|
||||||
case time.Time:
|
|
||||||
t.Time = input
|
|
||||||
return nil
|
|
||||||
case string:
|
|
||||||
var err error
|
|
||||||
t.Time, err = time.Parse(time.RFC3339, input)
|
|
||||||
return err
|
|
||||||
case []byte:
|
|
||||||
var err error
|
|
||||||
t.Time, err = time.Parse(time.RFC3339, string(input))
|
|
||||||
return err
|
|
||||||
case int32:
|
|
||||||
t.Time = time.Unix(int64(input), 0)
|
|
||||||
return nil
|
|
||||||
case int64:
|
|
||||||
if input >= 1e10 {
|
|
||||||
sec := input / 1e9
|
|
||||||
nsec := input - (sec * 1e9)
|
|
||||||
t.Time = time.Unix(sec, nsec)
|
|
||||||
} else {
|
|
||||||
t.Time = time.Unix(input, 0)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
case float64:
|
|
||||||
t.Time = time.Unix(int64(input), 0)
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("wrong type for Time: %T", input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON is a custom marshaler for Time
|
|
||||||
//
|
|
||||||
// This function will be called whenever you
|
|
||||||
// query for fields that use the Time type
|
|
||||||
func (t Time) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(t.Time)
|
|
||||||
}
|
|
96
vendor/github.com/graph-gophers/graphql-go/trace/trace.go
generated
vendored
96
vendor/github.com/graph-gophers/graphql-go/trace/trace.go
generated
vendored
@ -1,96 +0,0 @@
|
|||||||
package trace
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/graph-gophers/graphql-go/errors"
|
|
||||||
"github.com/graph-gophers/graphql-go/introspection"
|
|
||||||
opentracing "github.com/opentracing/opentracing-go"
|
|
||||||
"github.com/opentracing/opentracing-go/ext"
|
|
||||||
"github.com/opentracing/opentracing-go/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TraceQueryFinishFunc func([]*errors.QueryError)
|
|
||||||
type TraceFieldFinishFunc func(*errors.QueryError)
|
|
||||||
|
|
||||||
type Tracer interface {
|
|
||||||
TraceQuery(ctx context.Context, queryString string, operationName string, variables map[string]interface{}, varTypes map[string]*introspection.Type) (context.Context, TraceQueryFinishFunc)
|
|
||||||
TraceField(ctx context.Context, label, typeName, fieldName string, trivial bool, args map[string]interface{}) (context.Context, TraceFieldFinishFunc)
|
|
||||||
}
|
|
||||||
|
|
||||||
type OpenTracingTracer struct{}
|
|
||||||
|
|
||||||
func (OpenTracingTracer) TraceQuery(ctx context.Context, queryString string, operationName string, variables map[string]interface{}, varTypes map[string]*introspection.Type) (context.Context, TraceQueryFinishFunc) {
|
|
||||||
span, spanCtx := opentracing.StartSpanFromContext(ctx, "GraphQL request")
|
|
||||||
span.SetTag("graphql.query", queryString)
|
|
||||||
|
|
||||||
if operationName != "" {
|
|
||||||
span.SetTag("graphql.operationName", operationName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(variables) != 0 {
|
|
||||||
span.LogFields(log.Object("graphql.variables", variables))
|
|
||||||
}
|
|
||||||
|
|
||||||
return spanCtx, func(errs []*errors.QueryError) {
|
|
||||||
if len(errs) > 0 {
|
|
||||||
msg := errs[0].Error()
|
|
||||||
if len(errs) > 1 {
|
|
||||||
msg += fmt.Sprintf(" (and %d more errors)", len(errs)-1)
|
|
||||||
}
|
|
||||||
ext.Error.Set(span, true)
|
|
||||||
span.SetTag("graphql.error", msg)
|
|
||||||
}
|
|
||||||
span.Finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (OpenTracingTracer) TraceField(ctx context.Context, label, typeName, fieldName string, trivial bool, args map[string]interface{}) (context.Context, TraceFieldFinishFunc) {
|
|
||||||
if trivial {
|
|
||||||
return ctx, noop
|
|
||||||
}
|
|
||||||
|
|
||||||
span, spanCtx := opentracing.StartSpanFromContext(ctx, label)
|
|
||||||
span.SetTag("graphql.type", typeName)
|
|
||||||
span.SetTag("graphql.field", fieldName)
|
|
||||||
for name, value := range args {
|
|
||||||
span.SetTag("graphql.args."+name, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return spanCtx, func(err *errors.QueryError) {
|
|
||||||
if err != nil {
|
|
||||||
ext.Error.Set(span, true)
|
|
||||||
span.SetTag("graphql.error", err.Error())
|
|
||||||
}
|
|
||||||
span.Finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (OpenTracingTracer) TraceValidation(ctx context.Context) TraceValidationFinishFunc {
|
|
||||||
span, _ := opentracing.StartSpanFromContext(ctx, "Validate Query")
|
|
||||||
|
|
||||||
return func(errs []*errors.QueryError) {
|
|
||||||
if len(errs) > 0 {
|
|
||||||
msg := errs[0].Error()
|
|
||||||
if len(errs) > 1 {
|
|
||||||
msg += fmt.Sprintf(" (and %d more errors)", len(errs)-1)
|
|
||||||
}
|
|
||||||
ext.Error.Set(span, true)
|
|
||||||
span.SetTag("graphql.error", msg)
|
|
||||||
}
|
|
||||||
span.Finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func noop(*errors.QueryError) {}
|
|
||||||
|
|
||||||
type NoopTracer struct{}
|
|
||||||
|
|
||||||
func (NoopTracer) TraceQuery(ctx context.Context, queryString string, operationName string, variables map[string]interface{}, varTypes map[string]*introspection.Type) (context.Context, TraceQueryFinishFunc) {
|
|
||||||
return ctx, func(errs []*errors.QueryError) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (NoopTracer) TraceField(ctx context.Context, label, typeName, fieldName string, trivial bool, args map[string]interface{}) (context.Context, TraceFieldFinishFunc) {
|
|
||||||
return ctx, func(err *errors.QueryError) {}
|
|
||||||
}
|
|
25
vendor/github.com/graph-gophers/graphql-go/trace/validation_trace.go
generated
vendored
25
vendor/github.com/graph-gophers/graphql-go/trace/validation_trace.go
generated
vendored
@ -1,25 +0,0 @@
|
|||||||
package trace
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/graph-gophers/graphql-go/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TraceValidationFinishFunc = TraceQueryFinishFunc
|
|
||||||
|
|
||||||
// Deprecated: use ValidationTracerContext.
|
|
||||||
type ValidationTracer interface {
|
|
||||||
TraceValidation() TraceValidationFinishFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
type ValidationTracerContext interface {
|
|
||||||
TraceValidation(ctx context.Context) TraceValidationFinishFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
type NoopValidationTracer struct{}
|
|
||||||
|
|
||||||
// Deprecated: use a Tracer which implements ValidationTracerContext.
|
|
||||||
func (NoopValidationTracer) TraceValidation() TraceValidationFinishFunc {
|
|
||||||
return func(errs []*errors.QueryError) {}
|
|
||||||
}
|
|
44
vendor/github.com/graph-gophers/graphql-go/types/argument.go
generated
vendored
44
vendor/github.com/graph-gophers/graphql-go/types/argument.go
generated
vendored
@ -1,44 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
// Argument is a representation of the GraphQL Argument.
|
|
||||||
//
|
|
||||||
// https://spec.graphql.org/draft/#sec-Language.Arguments
|
|
||||||
type Argument struct {
|
|
||||||
Name Ident
|
|
||||||
Value Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArgumentList is a collection of GraphQL Arguments.
|
|
||||||
type ArgumentList []*Argument
|
|
||||||
|
|
||||||
// Returns a Value in the ArgumentList by name.
|
|
||||||
func (l ArgumentList) Get(name string) (Value, bool) {
|
|
||||||
for _, arg := range l {
|
|
||||||
if arg.Name.Name == name {
|
|
||||||
return arg.Value, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustGet returns a Value in the ArgumentList by name.
|
|
||||||
// MustGet will panic if the argument name is not found in the ArgumentList.
|
|
||||||
func (l ArgumentList) MustGet(name string) Value {
|
|
||||||
value, ok := l.Get(name)
|
|
||||||
if !ok {
|
|
||||||
panic("argument not found")
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
type ArgumentsDefinition []*InputValueDefinition
|
|
||||||
|
|
||||||
// Get returns an InputValueDefinition in the ArgumentsDefinition by name or nil if not found.
|
|
||||||
func (a ArgumentsDefinition) Get(name string) *InputValueDefinition {
|
|
||||||
for _, inputValue := range a {
|
|
||||||
if inputValue.Name.Name == name {
|
|
||||||
return inputValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
34
vendor/github.com/graph-gophers/graphql-go/types/directive.go
generated
vendored
34
vendor/github.com/graph-gophers/graphql-go/types/directive.go
generated
vendored
@ -1,34 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import "github.com/graph-gophers/graphql-go/errors"
|
|
||||||
|
|
||||||
// Directive is a representation of the GraphQL Directive.
|
|
||||||
//
|
|
||||||
// http://spec.graphql.org/draft/#sec-Language.Directives
|
|
||||||
type Directive struct {
|
|
||||||
Name Ident
|
|
||||||
Arguments ArgumentList
|
|
||||||
}
|
|
||||||
|
|
||||||
// DirectiveDefinition is a representation of the GraphQL DirectiveDefinition.
|
|
||||||
//
|
|
||||||
// http://spec.graphql.org/draft/#sec-Type-System.Directives
|
|
||||||
type DirectiveDefinition struct {
|
|
||||||
Name string
|
|
||||||
Desc string
|
|
||||||
Locations []string
|
|
||||||
Arguments ArgumentsDefinition
|
|
||||||
Loc errors.Location
|
|
||||||
}
|
|
||||||
|
|
||||||
type DirectiveList []*Directive
|
|
||||||
|
|
||||||
// Returns the Directive in the DirectiveList by name or nil if not found.
|
|
||||||
func (l DirectiveList) Get(name string) *Directive {
|
|
||||||
for _, d := range l {
|
|
||||||
if d.Name.Name == name {
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
9
vendor/github.com/graph-gophers/graphql-go/types/doc.go
generated
vendored
9
vendor/github.com/graph-gophers/graphql-go/types/doc.go
generated
vendored
@ -1,9 +0,0 @@
|
|||||||
/*
|
|
||||||
Package types represents all types from the GraphQL specification in code.
|
|
||||||
|
|
||||||
|
|
||||||
The names of the Go types, whenever possible, match 1:1 with the names from
|
|
||||||
the specification.
|
|
||||||
|
|
||||||
*/
|
|
||||||
package types
|
|
32
vendor/github.com/graph-gophers/graphql-go/types/enum.go
generated
vendored
32
vendor/github.com/graph-gophers/graphql-go/types/enum.go
generated
vendored
@ -1,32 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import "github.com/graph-gophers/graphql-go/errors"
|
|
||||||
|
|
||||||
// EnumTypeDefinition defines a set of possible enum values.
|
|
||||||
//
|
|
||||||
// Like scalar types, an EnumTypeDefinition also represents a leaf value in a GraphQL type system.
|
|
||||||
//
|
|
||||||
// http://spec.graphql.org/draft/#sec-Enums
|
|
||||||
type EnumTypeDefinition struct {
|
|
||||||
Name string
|
|
||||||
EnumValuesDefinition []*EnumValueDefinition
|
|
||||||
Desc string
|
|
||||||
Directives DirectiveList
|
|
||||||
Loc errors.Location
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnumValueDefinition are unique values that may be serialized as a string: the name of the
|
|
||||||
// represented value.
|
|
||||||
//
|
|
||||||
// http://spec.graphql.org/draft/#EnumValueDefinition
|
|
||||||
type EnumValueDefinition struct {
|
|
||||||
EnumValue string
|
|
||||||
Directives DirectiveList
|
|
||||||
Desc string
|
|
||||||
Loc errors.Location
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*EnumTypeDefinition) Kind() string { return "ENUM" }
|
|
||||||
func (t *EnumTypeDefinition) String() string { return t.Name }
|
|
||||||
func (t *EnumTypeDefinition) TypeName() string { return t.Name }
|
|
||||||
func (t *EnumTypeDefinition) Description() string { return t.Desc }
|
|
13
vendor/github.com/graph-gophers/graphql-go/types/extension.go
generated
vendored
13
vendor/github.com/graph-gophers/graphql-go/types/extension.go
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import "github.com/graph-gophers/graphql-go/errors"
|
|
||||||
|
|
||||||
// Extension type defines a GraphQL type extension.
|
|
||||||
// Schemas, Objects, Inputs and Scalars can be extended.
|
|
||||||
//
|
|
||||||
// https://spec.graphql.org/draft/#sec-Type-System-Extensions
|
|
||||||
type Extension struct {
|
|
||||||
Type NamedType
|
|
||||||
Directives DirectiveList
|
|
||||||
Loc errors.Location
|
|
||||||
}
|
|
39
vendor/github.com/graph-gophers/graphql-go/types/field.go
generated
vendored
39
vendor/github.com/graph-gophers/graphql-go/types/field.go
generated
vendored
@ -1,39 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import "github.com/graph-gophers/graphql-go/errors"
|
|
||||||
|
|
||||||
// FieldDefinition is a representation of a GraphQL FieldDefinition.
|
|
||||||
//
|
|
||||||
// http://spec.graphql.org/draft/#FieldDefinition
|
|
||||||
type FieldDefinition struct {
|
|
||||||
Name string
|
|
||||||
Arguments ArgumentsDefinition
|
|
||||||
Type Type
|
|
||||||
Directives DirectiveList
|
|
||||||
Desc string
|
|
||||||
Loc errors.Location
|
|
||||||
}
|
|
||||||
|
|
||||||
// FieldsDefinition is a list of an ObjectTypeDefinition's Fields.
|
|
||||||
//
|
|
||||||
// https://spec.graphql.org/draft/#FieldsDefinition
|
|
||||||
type FieldsDefinition []*FieldDefinition
|
|
||||||
|
|
||||||
// Get returns a FieldDefinition in a FieldsDefinition by name or nil if not found.
|
|
||||||
func (l FieldsDefinition) Get(name string) *FieldDefinition {
|
|
||||||
for _, f := range l {
|
|
||||||
if f.Name == name {
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Names returns a slice of FieldDefinition names.
|
|
||||||
func (l FieldsDefinition) Names() []string {
|
|
||||||
names := make([]string, len(l))
|
|
||||||
for i, f := range l {
|
|
||||||
names[i] = f.Name
|
|
||||||
}
|
|
||||||
return names
|
|
||||||
}
|
|
51
vendor/github.com/graph-gophers/graphql-go/types/fragment.go
generated
vendored
51
vendor/github.com/graph-gophers/graphql-go/types/fragment.go
generated
vendored
@ -1,51 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import "github.com/graph-gophers/graphql-go/errors"
|
|
||||||
|
|
||||||
type Fragment struct {
|
|
||||||
On TypeName
|
|
||||||
Selections SelectionSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// InlineFragment is a representation of the GraphQL InlineFragment.
|
|
||||||
//
|
|
||||||
// http://spec.graphql.org/draft/#InlineFragment
|
|
||||||
type InlineFragment struct {
|
|
||||||
Fragment
|
|
||||||
Directives DirectiveList
|
|
||||||
Loc errors.Location
|
|
||||||
}
|
|
||||||
|
|
||||||
// FragmentDefinition is a representation of the GraphQL FragmentDefinition.
|
|
||||||
//
|
|
||||||
// http://spec.graphql.org/draft/#FragmentDefinition
|
|
||||||
type FragmentDefinition struct {
|
|
||||||
Fragment
|
|
||||||
Name Ident
|
|
||||||
Directives DirectiveList
|
|
||||||
Loc errors.Location
|
|
||||||
}
|
|
||||||
|
|
||||||
// FragmentSpread is a representation of the GraphQL FragmentSpread.
|
|
||||||
//
|
|
||||||
// http://spec.graphql.org/draft/#FragmentSpread
|
|
||||||
type FragmentSpread struct {
|
|
||||||
Name Ident
|
|
||||||
Directives DirectiveList
|
|
||||||
Loc errors.Location
|
|
||||||
}
|
|
||||||
|
|
||||||
type FragmentList []*FragmentDefinition
|
|
||||||
|
|
||||||
// Returns a FragmentDefinition by name or nil if not found.
|
|
||||||
func (l FragmentList) Get(name string) *FragmentDefinition {
|
|
||||||
for _, f := range l {
|
|
||||||
if f.Name.Name == name {
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (InlineFragment) isSelection() {}
|
|
||||||
func (FragmentSpread) isSelection() {}
|
|
47
vendor/github.com/graph-gophers/graphql-go/types/input.go
generated
vendored
47
vendor/github.com/graph-gophers/graphql-go/types/input.go
generated
vendored
@ -1,47 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import "github.com/graph-gophers/graphql-go/errors"
|
|
||||||
|
|
||||||
// InputValueDefinition is a representation of the GraphQL InputValueDefinition.
|
|
||||||
//
|
|
||||||
// http://spec.graphql.org/draft/#InputValueDefinition
|
|
||||||
type InputValueDefinition struct {
|
|
||||||
Name Ident
|
|
||||||
Type Type
|
|
||||||
Default Value
|
|
||||||
Desc string
|
|
||||||
Directives DirectiveList
|
|
||||||
Loc errors.Location
|
|
||||||
TypeLoc errors.Location
|
|
||||||
}
|
|
||||||
|
|
||||||
type InputValueDefinitionList []*InputValueDefinition
|
|
||||||
|
|
||||||
// Returns an InputValueDefinition by name or nil if not found.
|
|
||||||
func (l InputValueDefinitionList) Get(name string) *InputValueDefinition {
|
|
||||||
for _, v := range l {
|
|
||||||
if v.Name.Name == name {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputObject types define a set of input fields; the input fields are either scalars, enums, or
|
|
||||||
// other input objects.
|
|
||||||
//
|
|
||||||
// This allows arguments to accept arbitrarily complex structs.
|
|
||||||
//
|
|
||||||
// http://spec.graphql.org/draft/#sec-Input-Objects
|
|
||||||
type InputObject struct {
|
|
||||||
Name string
|
|
||||||
Desc string
|
|
||||||
Values ArgumentsDefinition
|
|
||||||
Directives DirectiveList
|
|
||||||
Loc errors.Location
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*InputObject) Kind() string { return "INPUT_OBJECT" }
|
|
||||||
func (t *InputObject) String() string { return t.Name }
|
|
||||||
func (t *InputObject) TypeName() string { return t.Name }
|
|
||||||
func (t *InputObject) Description() string { return t.Desc }
|
|
25
vendor/github.com/graph-gophers/graphql-go/types/interface.go
generated
vendored
25
vendor/github.com/graph-gophers/graphql-go/types/interface.go
generated
vendored
@ -1,25 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import "github.com/graph-gophers/graphql-go/errors"
|
|
||||||
|
|
||||||
// InterfaceTypeDefinition recusrively defines list of named fields with their arguments via the
|
|
||||||
// implementation chain of interfaces.
|
|
||||||
//
|
|
||||||
// GraphQL objects can then implement these interfaces which requires that the object type will
|
|
||||||
// define all fields defined by those interfaces.
|
|
||||||
//
|
|
||||||
// http://spec.graphql.org/draft/#sec-Interfaces
|
|
||||||
type InterfaceTypeDefinition struct {
|
|
||||||
Name string
|
|
||||||
PossibleTypes []*ObjectTypeDefinition
|
|
||||||
Fields FieldsDefinition
|
|
||||||
Desc string
|
|
||||||
Directives DirectiveList
|
|
||||||
Loc errors.Location
|
|
||||||
Interfaces []*InterfaceTypeDefinition
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*InterfaceTypeDefinition) Kind() string { return "INTERFACE" }
|
|
||||||
func (t *InterfaceTypeDefinition) String() string { return t.Name }
|
|
||||||
func (t *InterfaceTypeDefinition) TypeName() string { return t.Name }
|
|
||||||
func (t *InterfaceTypeDefinition) Description() string { return t.Desc }
|
|
25
vendor/github.com/graph-gophers/graphql-go/types/object.go
generated
vendored
25
vendor/github.com/graph-gophers/graphql-go/types/object.go
generated
vendored
@ -1,25 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import "github.com/graph-gophers/graphql-go/errors"
|
|
||||||
|
|
||||||
// ObjectTypeDefinition represents a GraphQL ObjectTypeDefinition.
|
|
||||||
//
|
|
||||||
// type FooObject {
|
|
||||||
// foo: String
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// https://spec.graphql.org/draft/#sec-Objects
|
|
||||||
type ObjectTypeDefinition struct {
|
|
||||||
Name string
|
|
||||||
Interfaces []*InterfaceTypeDefinition
|
|
||||||
Fields FieldsDefinition
|
|
||||||
Desc string
|
|
||||||
Directives DirectiveList
|
|
||||||
InterfaceNames []string
|
|
||||||
Loc errors.Location
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*ObjectTypeDefinition) Kind() string { return "OBJECT" }
|
|
||||||
func (t *ObjectTypeDefinition) String() string { return t.Name }
|
|
||||||
func (t *ObjectTypeDefinition) TypeName() string { return t.Name }
|
|
||||||
func (t *ObjectTypeDefinition) Description() string { return t.Desc }
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user