cli: expose method to get iconsistent metadata objects

https://github.com/hasura/graphql-engine-mono/pull/1725

GitOrigin-RevId: 68c64369dcc950def19bf773ab1bc95226ebcdd4
This commit is contained in:
Aravind K P 2021-07-06 15:06:32 +05:30 committed by hasura-bot
parent 614c0dab80
commit e99c012af6
7 changed files with 198 additions and 14 deletions

View File

@ -120,26 +120,20 @@ func (c *ClientCommonMetadataOps) ReplaceMetadata(metadata io.Reader) (io.Reader
}
func (c *ClientCommonMetadataOps) GetInconsistentMetadata() (*hasura.GetInconsistentMetadataResponse, error) {
request := hasura.RequestBody{
Type: "get_inconsistent_metadata",
Args: map[string]string{},
}
responseBody := new(bytes.Buffer)
response, err := c.send(request, responseBody)
inconsistentMetadata := new(hasura.GetInconsistentMetadataResponse)
responseBody, err := c.GetInconsistentMetadataRaw()
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%s", responseBody.String())
}
inconsistentMetadata := new(hasura.GetInconsistentMetadataResponse)
if err := json.NewDecoder(responseBody).Decode(inconsistentMetadata); err != nil {
return nil, fmt.Errorf("decoding response: %w", err)
}
return inconsistentMetadata, nil
}
func (c *ClientCommonMetadataOps) GetInconsistentMetadataReader() (io.Reader, error) {
// GetInconsistentMetadataRaw
// https://hasura.io/docs/latest/graphql/core/api-reference/metadata-api/manage-metadata.html#metadata-get-inconsistent-metadata
func (c *ClientCommonMetadataOps) GetInconsistentMetadataRaw() (io.Reader, error) {
request := hasura.RequestBody{
Type: "get_inconsistent_metadata",
Args: map[string]string{},

View File

@ -6,8 +6,7 @@ import (
"github.com/hasura/graphql-engine/cli/v2/internal/httpc"
)
// general hasura metadata API requests
// these are not dependent on the connected source type
// CommonMetadataOperations represents Metadata API's which are not source type specific
type CommonMetadataOperations interface {
ExportMetadata() (metadata io.Reader, err error)
ClearMetadata() (io.Reader, error)
@ -15,7 +14,7 @@ type CommonMetadataOperations interface {
DropInconsistentMetadata() (io.Reader, error)
ReplaceMetadata(metadata io.Reader) (io.Reader, error)
GetInconsistentMetadata() (*GetInconsistentMetadataResponse, error)
GetInconsistentMetadataReader() (io.Reader, error)
GetInconsistentMetadataRaw() (io.Reader, error)
SendCommonMetadataOperation(requestBody interface{}) (httpcResponse *httpc.Response, body io.Reader, error error)
}

View File

@ -59,6 +59,11 @@ func (p *ProjectMetadata) Reload() (io.Reader, error) {
return metadataHandler.ReloadMetadata()
}
// GetInconsistentMetadata objects from hge server
func (p *ProjectMetadata) GetInconsistentMetadata() (io.Reader, error) {
return cli.GetCommonMetadataOps(p.ec).GetInconsistentMetadataRaw()
}
// Diff will return the differences between metadata in the project (in JSON) and on the server
func (p *ProjectMetadata) Diff() (io.Reader, error) {
w := new(bytes.Buffer)

View File

@ -1,10 +1,18 @@
package metadata
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"path/filepath"
"strings"
"testing"
"github.com/hasura/graphql-engine/cli/v2/internal/hasura"
"github.com/hasura/graphql-engine/cli/v2/pkg/migrate"
"github.com/stretchr/testify/require"
"github.com/hasura/graphql-engine/cli/v2/internal/testutil"
@ -204,3 +212,113 @@ func TestProjectMetadata_Reload(t *testing.T) {
})
}
}
func TestProjectMetadata_GetInconsistentMetadata(t *testing.T) {
type before func(t *testing.T, p *ProjectMetadata, m *migrate.ProjectMigrate, hgePort, queryEndpoint string)
configV2Before := func(t *testing.T, metadata *ProjectMetadata, migrations *migrate.ProjectMigrate, hgePort, queryEndpoint string) {
// - apply all migrations
// - apply metadata
// - drop a table via run_sql API
// - reload metadata
err := migrations.Apply(migrate.ApplyOnAllDatabases())
require.NoError(t, err)
_, err = metadata.Apply()
require.NoError(t, err)
// remove a table from database
c := testutil.NewHttpcClient(t, hgePort, nil)
r, err := c.NewRequest(
http.MethodPost,
queryEndpoint,
hasura.RequestBody{
Type: "run_sql",
Args: hasura.PGRunSQLInput{
SQL: "DROP table t1;",
CheckMetadataConsistency: func() *bool { var v = false; return &v }(),
},
},
)
require.NoError(t, err)
resp, err := c.Do(context.Background(), r, nil)
require.Equal(t, http.StatusOK, resp.StatusCode)
require.NoError(t, err)
_, err = metadata.Reload()
require.NoError(t, err)
}
type fields struct {
projectDirectory string
}
tests := []struct {
name string
fields fields
before before
hasuraImage string
queryEndpoint string
wantErr bool
}{
{
"can list inconsistent metadata config v3",
fields{
projectDirectory: "testdata/projectv3",
},
func(t *testing.T, metadata *ProjectMetadata, _ *migrate.ProjectMigrate, _ string, _ string) {
_, err := metadata.Apply()
require.NoError(t, err)
},
testutil.HasuraDockerImage,
"v2/query",
false,
},
{
"can list inconsistent metadata config v2",
fields{
projectDirectory: "testdata/projectv2",
},
configV2Before,
testutil.HasuraDockerImage,
"v2/query",
false,
},
{
"can list inconsistent metadata config v2 v1.3.3",
fields{
projectDirectory: "testdata/projectv2",
},
configV2Before,
"hasura/graphql-engine:v1.3.3",
"v1/query",
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
port, teardown := testutil.StartHasura(t, tt.hasuraImage)
hgeEndpoint := fmt.Sprintf("%s:%s", testutil.BaseURL, port)
defer teardown()
migrations, err := migrate.NewProjectMigrate(tt.fields.projectDirectory, migrate.WithEndpoint(hgeEndpoint), migrate.WithAdminSecret(testutil.TestAdminSecret))
require.NoError(t, err)
metadata, err := NewProjectMetadata(tt.fields.projectDirectory, WithEndpoint(hgeEndpoint), WithAdminSecret(testutil.TestAdminSecret))
require.NoError(t, err)
if tt.before != nil {
tt.before(t, metadata, migrations, port, tt.queryEndpoint)
}
got, err := metadata.GetInconsistentMetadata()
if tt.wantErr {
require.Error(t, err)
}
require.NoError(t, err)
gotb, err := ioutil.ReadAll(got)
require.NoError(t, err)
goldenFile := filepath.Join("testdata/get_inconsistent_metadata_test", strings.Join(strings.Split(tt.name, " "), "_")+".golden.json")
// uncomment the following line to update test golden file
// require.NoError(t, ioutil.WriteFile(goldenFile, gotb, 0655))
wantb, err := ioutil.ReadFile(goldenFile)
require.NoError(t, err)
require.JSONEq(t, string(wantb), string(gotb))
})
}
}

View File

@ -0,0 +1,14 @@
{
"is_consistent": false,
"inconsistent_objects": [
{
"definition": {
"schema": "public",
"name": "t1"
},
"reason": "Inconsistent object: no such table/view exists in source: \"t1\"",
"name": "table t1 in source default",
"type": "table"
}
]
}

View File

@ -0,0 +1,13 @@
{
"is_consistent": false,
"inconsistent_objects": [
{
"definition": {
"schema": "public",
"name": "t1"
},
"reason": "no such table/view exists in postgres: \"t1\"",
"type": "table"
}
]
}

View File

@ -0,0 +1,41 @@
{
"is_consistent": false,
"inconsistent_objects": [
{
"definition": {
"schema": "public",
"name": "t1"
},
"reason": "Inconsistent object: no such table/view exists in source: \"t1\"",
"name": "table t1 in source default",
"type": "table"
},
{
"definition": {
"schema": "public",
"name": "t2"
},
"reason": "Inconsistent object: no such table/view exists in source: \"t2\"",
"name": "table t2 in source default",
"type": "table"
},
{
"definition": {
"schema": "pub",
"name": "t4"
},
"reason": "Inconsistent object: no such table/view exists in source: \"pub.t4\"",
"name": "table pub.t4 in source default",
"type": "table"
},
{
"definition": {
"schema": "pub",
"name": "t3"
},
"reason": "Inconsistent object: no such table/view exists in source: \"pub.t3\"",
"name": "table pub.t3 in source default",
"type": "table"
}
]
}