Skip to content

Commit

Permalink
feat: add schema history to graphql (#6324)
Browse files Browse the repository at this point in the history
  • Loading branch information
poonai authored Sep 8, 2020
1 parent c8fee85 commit d9e95a9
Show file tree
Hide file tree
Showing 20 changed files with 523 additions and 161 deletions.
2 changes: 2 additions & 0 deletions dgraph/cmd/bulk/systest/test-bulk-schema.sh
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ EOF
1 dgraph.acl.rule
1 dgraph.cors
1 dgraph.graphql.schema
1 dgraph.graphql.schema_created_at
1 dgraph.graphql.schema_history
1 dgraph.graphql.xid
1 dgraph.password
1 dgraph.rule.permission
Expand Down
153 changes: 153 additions & 0 deletions edgraph/graphql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Copyright 2017-2020 Dgraph Labs, Inc. and Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package edgraph

import (
"bytes"
"context"
"encoding/json"
"fmt"
"time"

"github.com/dgraph-io/dgo/v200/protos/api"
"github.com/dgraph-io/ristretto/z"
"github.com/golang/glog"
)

// ResetCors make the dgraph to accept all the origins if no origins were given
// by the users.
func ResetCors(closer *z.Closer) {
defer func() {
glog.Infof("ResetCors closed")
closer.Done()
}()

req := &api.Request{
Query: `query{
cors as var(func: has(dgraph.cors))
}`,
Mutations: []*api.Mutation{
{
Set: []*api.NQuad{
{
Subject: "_:a",
Predicate: "dgraph.cors",
ObjectValue: &api.Value{Val: &api.Value_StrVal{StrVal: "*"}},
},
},
Cond: `@if(eq(len(cors), 0))`,
},
},
CommitNow: true,
}

for closer.Ctx().Err() == nil {
ctx, cancel := context.WithTimeout(closer.Ctx(), time.Minute)
defer cancel()
ctx = context.WithValue(ctx, IsGraphql, true)
if _, err := (&Server{}).doQuery(ctx, req, NoAuthorize); err != nil {
glog.Infof("Unable to upsert cors. Error: %v", err)
time.Sleep(100 * time.Millisecond)
}
break
}
}

func generateNquadsForCors(origins []string) []byte {
out := &bytes.Buffer{}
for _, origin := range origins {
out.Write([]byte(fmt.Sprintf("uid(cors) <dgraph.cors> \"%s\" . \n", origin)))
}
return out.Bytes()
}

// AddCorsOrigins Adds the cors origins to the Dgraph.
func AddCorsOrigins(ctx context.Context, origins []string) error {
req := &api.Request{
Query: `query{
cors as var(func: has(dgraph.cors))
}`,
Mutations: []*api.Mutation{
{
SetNquads: generateNquadsForCors(origins),
Cond: `@if(eq(len(cors), 1))`,
DelNquads: []byte(`uid(cors) <dgraph.cors> * .`),
},
},
CommitNow: true,
}
_, err := (&Server{}).doQuery(context.WithValue(ctx, IsGraphql, true), req, NoAuthorize)
return err
}

// GetCorsOrigins retrive all the cors origin from the database.
func GetCorsOrigins(ctx context.Context) ([]string, error) {
req := &api.Request{
Query: `query{
me(func: has(dgraph.cors)){
dgraph.cors
}
}`,
ReadOnly: true,
}
res, err := (&Server{}).doQuery(context.WithValue(ctx, IsGraphql, true), req, NoAuthorize)
if err != nil {
return nil, err
}

type corsResponse struct {
Me []struct {
DgraphCors []string `json:"dgraph.cors"`
} `json:"me"`
}
corsRes := &corsResponse{}
if err = json.Unmarshal(res.Json, corsRes); err != nil {
return nil, err
}
if len(corsRes.Me) != 1 {
return []string{}, fmt.Errorf("GetCorsOrigins returned %d results", len(corsRes.Me))
}
return corsRes.Me[0].DgraphCors, nil
}

// UpdateSchemaHistory updates graphql schema history.
func UpdateSchemaHistory(ctx context.Context, schema string) error {
req := &api.Request{
Mutations: []*api.Mutation{
{
Set: []*api.NQuad{
{
Subject: "_:a",
Predicate: "dgraph.graphql.schema_history",
ObjectValue: &api.Value{Val: &api.Value_StrVal{StrVal: schema}},
},
{
Subject: "_:a",
Predicate: "dgraph.type",
ObjectValue: &api.Value{Val: &api.Value_StrVal{
StrVal: "dgraph.graphql.history"}},
},
},
SetNquads: []byte(fmt.Sprintf(`_:a <dgraph.graphql.schema_created_at> "%s" .`,
time.Now().Format(time.RFC3339))),
},
},
CommitNow: true,
}
_, err := (&Server{}).doQuery(context.WithValue(ctx, IsGraphql, true), req, NoAuthorize)
return err
}
127 changes: 0 additions & 127 deletions edgraph/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ import (
"github.com/dgraph-io/dgraph/types/facets"
"github.com/dgraph-io/dgraph/worker"
"github.com/dgraph-io/dgraph/x"
"github.com/dgraph-io/ristretto/z"
)

const (
Expand All @@ -85,9 +84,6 @@ const (
// NoAuthorize is used to indicate that authorization needs to be skipped.
// Used when ACL needs to query information for performing the authorization check.
NoAuthorize
// CorsMutationAllowed is used to indicate that the given request is authorized to do
// cors mutation.
CorsMutationAllowed
)

var (
Expand Down Expand Up @@ -989,11 +985,6 @@ func (s *Server) doQuery(ctx context.Context, req *api.Request, doAuth AuthMode)
}
}

if doAuth != CorsMutationAllowed {
if rerr = validateCorsInMutation(ctx, qc); rerr != nil {
return
}
}
// We use defer here because for queries, startTs will be
// assigned in the processQuery function called below.
defer annotateStartTs(qc.span, qc.req.StartTs)
Expand Down Expand Up @@ -1231,29 +1222,6 @@ func authorizeRequest(ctx context.Context, qc *queryContext) error {
return nil
}

// validateCorsInMutation check whether mutation contains cors predication. If it's contain cors
// predicate, we'll throw an error.
func validateCorsInMutation(ctx context.Context, qc *queryContext) error {
validateNquad := func(nquads []*api.NQuad) error {
for _, nquad := range nquads {
if nquad.Predicate != "dgraph.cors" {
continue
}
return errors.New("Mutations are not allowed for the predicate dgraph.cors")
}
return nil
}
for _, gmu := range qc.gmuList {
if err := validateNquad(gmu.Set); err != nil {
return err
}
if err := validateNquad(gmu.Del); err != nil {
return err
}
}
return nil
}

// CommitOrAbort commits or aborts a transaction.
func (s *Server) CommitOrAbort(ctx context.Context, tc *api.TxnContext) (*api.TxnContext, error) {
ctx, span := otrace.StartSpan(ctx, "Server.CommitOrAbort")
Expand Down Expand Up @@ -1548,98 +1516,3 @@ func isDropAll(op *api.Operation) bool {
}
return false
}

// ResetCors make the dgraph to accept all the origins if no origins were given
// by the users.
func ResetCors(closer *z.Closer) {
defer func() {
glog.Infof("ResetCors closed")
closer.Done()
}()

req := &api.Request{
Query: `query{
cors as var(func: has(dgraph.cors))
}`,
Mutations: []*api.Mutation{
{
Set: []*api.NQuad{
{
Subject: "_:a",
Predicate: "dgraph.cors",
ObjectValue: &api.Value{Val: &api.Value_StrVal{StrVal: "*"}},
},
},
Cond: `@if(eq(len(cors), 0))`,
},
},
CommitNow: true,
}

for closer.Ctx().Err() == nil {
ctx, cancel := context.WithTimeout(closer.Ctx(), time.Minute)
defer cancel()
if _, err := (&Server{}).doQuery(ctx, req, CorsMutationAllowed); err != nil {
glog.Infof("Unable to upsert cors. Error: %v", err)
time.Sleep(100 * time.Millisecond)
}
break
}
}

func generateNquadsForCors(origins []string) []byte {
out := &bytes.Buffer{}
for _, origin := range origins {
out.Write([]byte(fmt.Sprintf("uid(cors) <dgraph.cors> \"%s\" . \n", origin)))
}
return out.Bytes()
}

// AddCorsOrigins Adds the cors origins to the Dgraph.
func AddCorsOrigins(ctx context.Context, origins []string) error {
req := &api.Request{
Query: `query{
cors as var(func: has(dgraph.cors))
}`,
Mutations: []*api.Mutation{
{
SetNquads: generateNquadsForCors(origins),
Cond: `@if(eq(len(cors), 1))`,
DelNquads: []byte(`uid(cors) <dgraph.cors> * .`),
},
},
CommitNow: true,
}
_, err := (&Server{}).doQuery(ctx, req, CorsMutationAllowed)
return err
}

// GetCorsOrigins retrive all the cors origin from the database.
func GetCorsOrigins(ctx context.Context) ([]string, error) {
req := &api.Request{
Query: `query{
me(func: has(dgraph.cors)){
dgraph.cors
}
}`,
ReadOnly: true,
}
res, err := (&Server{}).doQuery(ctx, req, NoAuthorize)
if err != nil {
return nil, err
}

type corsResponse struct {
Me []struct {
DgraphCors []string `json:"dgraph.cors"`
} `json:"me"`
}
corsRes := &corsResponse{}
if err = json.Unmarshal(res.Json, corsRes); err != nil {
return nil, err
}
if len(corsRes.Me) != 1 {
return []string{}, fmt.Errorf("GetCorsOrigins returned %d results", len(corsRes.Me))
}
return corsRes.Me[0].DgraphCors, nil
}
28 changes: 25 additions & 3 deletions ee/acl/acl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2093,7 +2093,15 @@ func TestSchemaQueryWithACL(t *testing.T) {
{
"predicate": "dgraph.graphql.schema",
"type": "string"
},
},
{
"predicate": "dgraph.graphql.schema_created_at",
"type": "datetime"
},
{
"predicate": "dgraph.graphql.schema_history",
"type": "string"
},
{
"predicate": "dgraph.graphql.xid",
"type": "string",
Expand Down Expand Up @@ -2156,7 +2164,17 @@ func TestSchemaQueryWithACL(t *testing.T) {
}
],
"name": "dgraph.graphql"
},
},
{
"fields": [
{
"name": "dgraph.graphql.schema_history"
},{
"name": "dgraph.graphql.schema_created_at"
}
],
"name": "dgraph.graphql.history"
},
{
"fields": [
{
Expand Down Expand Up @@ -2210,7 +2228,11 @@ func TestSchemaQueryWithACL(t *testing.T) {
{
"fields": [],
"name": "dgraph.graphql"
},
},
{
"fields": [],
"name": "dgraph.graphql.history"
},
{
"fields": [],
"name": "dgraph.type.Group"
Expand Down
Loading

0 comments on commit d9e95a9

Please sign in to comment.