mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 08:02:15 +03:00
cli: fix remote schema formatting errors in metadata
closes https://github.com/hasura/graphql-engine/issues/7608 closes https://github.com/hasura/graphql-engine/issues/7459 PR-URL: https://github.com/hasura/graphql-engine-mono/pull/3951 GitOrigin-RevId: bad3505c79fab10453580a6a43ad7e71bc2d3753
This commit is contained in:
parent
0baef156df
commit
649ef41e3c
@ -4,6 +4,8 @@
|
||||
|
||||
### Bug fixes and improvements
|
||||
|
||||
- cli: fix remote schema metadata formatting issues (#7608)
|
||||
|
||||
## v2.5.0-beta.1
|
||||
|
||||
### Remote relationships from remote schemas
|
||||
|
@ -49,6 +49,7 @@ require (
|
||||
github.com/spf13/viper v1.10.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/subosito/gotenv v1.2.0
|
||||
github.com/vektah/gqlparser v1.3.1
|
||||
golang.org/x/term v0.0.0-20210916214954-140adaaadfaf
|
||||
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||
|
@ -83,6 +83,8 @@ github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdc
|
||||
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
|
||||
github.com/a8m/envsubst v1.3.0 h1:GmXKmVssap0YtlU3E230W98RWtWCyIZzjtf1apWWyAg=
|
||||
github.com/a8m/envsubst v1.3.0/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY=
|
||||
github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
github.com/ahmetb/go-linq v3.0.0+incompatible h1:qQkjjOXKrKOTy83X8OpRmnKflXKQIL/mC/gMVVDMhOA=
|
||||
github.com/ahmetb/go-linq v3.0.0+incompatible/go.mod h1:PFffvbdbtw+QTB0WKRP0cNht7vnCfnGlEpak/DVg5cY=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
@ -91,6 +93,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
@ -666,6 +670,8 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/vektah/gqlparser v1.3.1 h1:8b0IcD3qZKWJQHSzynbDlrtP3IxVydZ2DZepCGofqfU=
|
||||
github.com/vektah/gqlparser v1.3.1/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74=
|
||||
github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74 h1:JwtAtbp7r/7QSyGz8mKUbYJBg2+6Cd7OjM8o/GNOcVo=
|
||||
github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74/go.mod h1:RmMWU37GKR2s6pgrIEB4ixgpVCt/cf7dnJv3fuH1J1c=
|
||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||
@ -933,6 +939,7 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
|
@ -9,20 +9,12 @@ import (
|
||||
"github.com/hasura/graphql-engine/cli/v2"
|
||||
"github.com/hasura/graphql-engine/cli/v2/internal/metadataobject"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/vektah/gqlparser"
|
||||
"github.com/vektah/gqlparser/ast"
|
||||
"github.com/vektah/gqlparser/formatter"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type RemoteSchema struct {
|
||||
Name string `yaml:"name"`
|
||||
Definition interface{} `yaml:"definition"`
|
||||
Comment interface{} `yaml:"comment"`
|
||||
Permission interface{} `yaml:"permissions"`
|
||||
}
|
||||
|
||||
func (r RemoteSchema) BaseDirectory() string {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
type SchemaDefinition struct {
|
||||
Schema string `yaml:"schema"`
|
||||
}
|
||||
@ -75,8 +67,66 @@ func (r *RemoteSchemaConfig) Build() (map[string]interface{}, metadataobject.Err
|
||||
return map[string]interface{}{r.Key(): obj}, nil
|
||||
}
|
||||
|
||||
type remoteSchema struct {
|
||||
Name yaml.Node `yaml:"name,omitempty"`
|
||||
Defintion yaml.Node `yaml:"definition,omitempty"`
|
||||
Comment yaml.Node `yaml:"comment,omitempty"`
|
||||
Permissions []permission `yaml:"permissions,omitempty"`
|
||||
}
|
||||
|
||||
type permission struct {
|
||||
Role yaml.Node `yaml:"role,omitempty"`
|
||||
Definition definition `yaml:"definition,omitempty"`
|
||||
}
|
||||
|
||||
type definition struct {
|
||||
Schema string `yaml:"schema,omitempty"`
|
||||
}
|
||||
|
||||
func (r *RemoteSchemaConfig) Export(metadata map[string]yaml.Node) (map[string][]byte, metadataobject.ErrParsingMetadataObject) {
|
||||
return metadataobject.DefaultExport(r, metadata, r.error, metadataobject.DefaultObjectTypeSequence)
|
||||
var value interface{}
|
||||
if v, ok := metadata[r.Key()]; !ok {
|
||||
value = []yaml.Node{}
|
||||
} else {
|
||||
remoteSchemas := []remoteSchema{}
|
||||
bs, err := yaml.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, r.error(err)
|
||||
}
|
||||
if err := yaml.Unmarshal(bs, &remoteSchemas); err != nil {
|
||||
return nil, r.error(err)
|
||||
}
|
||||
|
||||
for rsIdx := range remoteSchemas {
|
||||
for pIdx := range remoteSchemas[rsIdx].Permissions {
|
||||
buf := new(bytes.Buffer)
|
||||
gqlFormatter := formatter.NewFormatter(buf)
|
||||
schema, err := gqlparser.LoadSchema(&ast.Source{
|
||||
Input: remoteSchemas[rsIdx].Permissions[pIdx].Definition.Schema,
|
||||
})
|
||||
if err != nil {
|
||||
r.logger.Infof("formatting permission for role %v in remote schema %v failed", remoteSchemas[rsIdx].Permissions[pIdx].Role, remoteSchemas[rsIdx].Name)
|
||||
r.logger.Debugf("loading schema failed for role: %v remote schema: %v error: %v", remoteSchemas[rsIdx].Permissions[pIdx].Role, remoteSchemas[rsIdx].Name, err)
|
||||
continue
|
||||
}
|
||||
gqlFormatter.FormatSchema(schema)
|
||||
if buf.Len() > 0 {
|
||||
remoteSchemas[rsIdx].Permissions[pIdx].Definition.Schema = buf.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value = remoteSchemas
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
err := metadataobject.GetEncoder(&buf).Encode(value)
|
||||
if err != nil {
|
||||
return nil, r.error(err)
|
||||
}
|
||||
return map[string][]byte{
|
||||
filepath.ToSlash(filepath.Join(r.BaseDirectory(), r.Filename())): buf.Bytes(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *RemoteSchemaConfig) GetFiles() ([]string, metadataobject.ErrParsingMetadataObject) {
|
||||
|
@ -43,6 +43,16 @@ func TestRemoteSchemaConfig_Build(t *testing.T) {
|
||||
"testdata/build_test/t2/want.golden.json",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"t3",
|
||||
"can build metadata json with multiline strings",
|
||||
fields{
|
||||
MetadataDir: "testdata/build_test/t3/metadata",
|
||||
logger: logrus.New(),
|
||||
},
|
||||
"testdata/build_test/t3/want.golden.json",
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@ -61,7 +71,7 @@ func TestRemoteSchemaConfig_Build(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
// uncomment following lines to update golden file
|
||||
//assert.NoError(t, ioutil.WriteFile(tt.wantGolden, jsonbs, os.ModePerm))
|
||||
// assert.NoError(t, ioutil.WriteFile(tt.wantGolden, jsonbs, os.ModePerm))
|
||||
|
||||
wantbs, err := ioutil.ReadFile(tt.wantGolden)
|
||||
assert.NoError(t, err)
|
||||
@ -170,6 +180,33 @@ func TestRemoteSchemaConfig_Export(t *testing.T) {
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"t4",
|
||||
"can export remote schema with multiline strings - 2",
|
||||
fields{
|
||||
MetadataDir: "metadata",
|
||||
logger: logrus.New(),
|
||||
},
|
||||
args{
|
||||
metadata: func() map[string]yaml.Node {
|
||||
bs, err := ioutil.ReadFile("testdata/export_test/t4/metadata.json")
|
||||
assert.NoError(t, err)
|
||||
yamlbs, err := metadatautil.JSONToYAML(bs)
|
||||
assert.NoError(t, err)
|
||||
var v map[string]yaml.Node
|
||||
assert.NoError(t, yaml.Unmarshal(yamlbs, &v))
|
||||
return v
|
||||
}(),
|
||||
},
|
||||
map[string][]byte{
|
||||
"metadata/remote_schemas.yaml": func() []byte {
|
||||
bs, err := ioutil.ReadFile("testdata/export_test/t4/want.remote_schemas.yaml")
|
||||
assert.NoError(t, err)
|
||||
return bs
|
||||
}(),
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@ -181,6 +218,7 @@ func TestRemoteSchemaConfig_Export(t *testing.T) {
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
for k, v := range got {
|
||||
assert.Contains(t, tt.want, k)
|
||||
// uncomment to update golden files
|
||||
|
57
cli/internal/metadataobject/remoteschemas/testdata/build_test/t3/metadata/remote_schemas.yaml
vendored
Executable file
57
cli/internal/metadataobject/remoteschemas/testdata/build_test/t3/metadata/remote_schemas.yaml
vendored
Executable file
@ -0,0 +1,57 @@
|
||||
- name: countries
|
||||
comment: foo countries
|
||||
permissions:
|
||||
- role: user
|
||||
definition:
|
||||
schema: |-
|
||||
schema { query: Query }
|
||||
|
||||
type Continent { code: ID!
|
||||
countries: [Country!]!
|
||||
name: String!
|
||||
}
|
||||
|
||||
type Country { capital: String
|
||||
code: ID!
|
||||
continent: Continent!
|
||||
currency: String
|
||||
emoji: String!
|
||||
emojiU: String!
|
||||
languages: [Language!]!
|
||||
name: String!
|
||||
native: String!
|
||||
phone: String!
|
||||
states: [State!]!
|
||||
}
|
||||
|
||||
type Language { code: ID!
|
||||
name: String
|
||||
native: String
|
||||
rtl: Boolean!
|
||||
}
|
||||
|
||||
type Query { continent(code: ID!): Continent
|
||||
countries(filter: CountryFilterInput): [Country!]!
|
||||
languages(filter: LanguageFilterInput): [Language!]!
|
||||
}
|
||||
|
||||
type State { code: String
|
||||
country: Country!
|
||||
name: String!
|
||||
}
|
||||
|
||||
input CountryFilterInput {code: StringQueryOperatorInput
|
||||
continent: StringQueryOperatorInput
|
||||
currency: StringQueryOperatorInput
|
||||
}
|
||||
|
||||
input LanguageFilterInput {code: StringQueryOperatorInput
|
||||
}
|
||||
|
||||
input StringQueryOperatorInput {eq: String
|
||||
glob: String
|
||||
in: [String]
|
||||
ne: String
|
||||
nin: [String]
|
||||
regex: String
|
||||
}
|
1
cli/internal/metadataobject/remoteschemas/testdata/build_test/t3/want.golden.json
vendored
Normal file
1
cli/internal/metadataobject/remoteschemas/testdata/build_test/t3/want.golden.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"remote_schemas": [{"name": "countries", "comment": "foo countries", "permissions": [{"role": "user", "definition": {"schema": "schema { query: Query }\n\ntype Continent { code: ID!\n countries: [Country!]!\n name: String!\n}\n\ntype Country { capital: String\n code: ID!\n continent: Continent!\n currency: String\n emoji: String!\n emojiU: String!\n languages: [Language!]!\n name: String!\n native: String!\n phone: String!\n states: [State!]!\n}\n\ntype Language { code: ID!\n name: String\n native: String\n rtl: Boolean!\n}\n\ntype Query { continent(code: ID!): Continent\n countries(filter: CountryFilterInput): [Country!]!\n languages(filter: LanguageFilterInput): [Language!]!\n}\n\ntype State { code: String\n country: Country!\n name: String!\n}\n\ninput CountryFilterInput {code: StringQueryOperatorInput\n continent: StringQueryOperatorInput\n currency: StringQueryOperatorInput\n}\n\ninput LanguageFilterInput {code: StringQueryOperatorInput\n}\n\ninput StringQueryOperatorInput {eq: String\n glob: String\n in: [String]\n ne: String\n nin: [String]\n regex: String\n}"}}]}]}
|
@ -10,55 +10,54 @@
|
||||
permissions:
|
||||
- role: user
|
||||
definition:
|
||||
schema: |-
|
||||
schema { query: Query }
|
||||
|
||||
type Continent { code: ID!
|
||||
countries: [Country!]!
|
||||
name: String!
|
||||
schema: |
|
||||
type Continent {
|
||||
code: ID!
|
||||
countries: [Country!]!
|
||||
name: String!
|
||||
}
|
||||
|
||||
type Country { capital: String
|
||||
code: ID!
|
||||
continent: Continent!
|
||||
currency: String
|
||||
emoji: String!
|
||||
emojiU: String!
|
||||
languages: [Language!]!
|
||||
name: String!
|
||||
native: String!
|
||||
phone: String!
|
||||
states: [State!]!
|
||||
type Country {
|
||||
capital: String
|
||||
code: ID!
|
||||
continent: Continent!
|
||||
currency: String
|
||||
emoji: String!
|
||||
emojiU: String!
|
||||
languages: [Language!]!
|
||||
name: String!
|
||||
native: String!
|
||||
phone: String!
|
||||
states: [State!]!
|
||||
}
|
||||
|
||||
type Language { code: ID!
|
||||
name: String
|
||||
native: String
|
||||
rtl: Boolean!
|
||||
input CountryFilterInput {
|
||||
code: StringQueryOperatorInput
|
||||
continent: StringQueryOperatorInput
|
||||
currency: StringQueryOperatorInput
|
||||
}
|
||||
|
||||
type Query { continent(code: ID!): Continent
|
||||
countries(filter: CountryFilterInput): [Country!]!
|
||||
languages(filter: LanguageFilterInput): [Language!]!
|
||||
type Language {
|
||||
code: ID!
|
||||
name: String
|
||||
native: String
|
||||
rtl: Boolean!
|
||||
}
|
||||
|
||||
type State { code: String
|
||||
country: Country!
|
||||
name: String!
|
||||
input LanguageFilterInput {
|
||||
code: StringQueryOperatorInput
|
||||
}
|
||||
|
||||
input CountryFilterInput {code: StringQueryOperatorInput
|
||||
continent: StringQueryOperatorInput
|
||||
currency: StringQueryOperatorInput
|
||||
type Query {
|
||||
continent(code: ID!): Continent
|
||||
countries(filter: CountryFilterInput): [Country!]!
|
||||
languages(filter: LanguageFilterInput): [Language!]!
|
||||
}
|
||||
|
||||
input LanguageFilterInput {code: StringQueryOperatorInput
|
||||
type State {
|
||||
code: String
|
||||
country: Country!
|
||||
name: String!
|
||||
}
|
||||
|
||||
input StringQueryOperatorInput {eq: String
|
||||
glob: String
|
||||
in: [String]
|
||||
ne: String
|
||||
nin: [String]
|
||||
regex: String
|
||||
input StringQueryOperatorInput {
|
||||
eq: String
|
||||
glob: String
|
||||
in: [String]
|
||||
ne: String
|
||||
nin: [String]
|
||||
regex: String
|
||||
}
|
||||
|
32
cli/internal/metadataobject/remoteschemas/testdata/export_test/t4/metadata.json
vendored
Normal file
32
cli/internal/metadataobject/remoteschemas/testdata/export_test/t4/metadata.json
vendored
Normal file
File diff suppressed because one or more lines are too long
1294
cli/internal/metadataobject/remoteschemas/testdata/export_test/t4/want.remote_schemas.yaml
vendored
Executable file
1294
cli/internal/metadataobject/remoteschemas/testdata/export_test/t4/want.remote_schemas.yaml
vendored
Executable file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user