d2ir: wip
This commit is contained in:
parent
fd241e4425
commit
989fdb0fe5
13 changed files with 308 additions and 39 deletions
|
|
@ -397,3 +397,16 @@ func (p *printer) edgeIndex(ei *d2ast.EdgeIndex) {
|
||||||
}
|
}
|
||||||
p.sb.WriteByte(']')
|
p.sb.WriteByte(']')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func KeyPath(kp *d2ast.KeyPath) []string {
|
||||||
|
var ids []string
|
||||||
|
for _, s := range kp.Path {
|
||||||
|
// We format each string of the key to ensure the resulting strings can be parsed
|
||||||
|
// correctly.
|
||||||
|
n := &d2ast.KeyPath{
|
||||||
|
Path: []*d2ast.StringBox{d2ast.MakeValueBox(d2ast.RawString(s.Unbox().ScalarString(), true)).StringBox()},
|
||||||
|
}
|
||||||
|
ids = append(ids, Format(n))
|
||||||
|
}
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1238,16 +1238,7 @@ func (g *Graph) Texts() []*d2target.MText {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Key(k *d2ast.KeyPath) []string {
|
func Key(k *d2ast.KeyPath) []string {
|
||||||
var ids []string
|
return d2format.KeyPath(k)
|
||||||
for _, s := range k.Path {
|
|
||||||
// We format each string of the key to ensure the resulting strings can be parsed
|
|
||||||
// correctly.
|
|
||||||
n := &d2ast.KeyPath{
|
|
||||||
Path: []*d2ast.StringBox{d2ast.MakeValueBox(d2ast.RawString(s.Unbox().ScalarString(), true)).StringBox()},
|
|
||||||
}
|
|
||||||
ids = append(ids, d2format.Format(n))
|
|
||||||
}
|
|
||||||
return ids
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ReservedKeywords = map[string]struct{}{
|
var ReservedKeywords = map[string]struct{}{
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/d2ast"
|
"oss.terrastruct.com/d2/d2ast"
|
||||||
"oss.terrastruct.com/d2/d2graph"
|
"oss.terrastruct.com/d2/d2format"
|
||||||
"oss.terrastruct.com/d2/d2parser"
|
"oss.terrastruct.com/d2/d2parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -23,28 +23,29 @@ func (c *compiler) errorf(n d2ast.Node, f string, v ...interface{}) {
|
||||||
|
|
||||||
func Apply(dst *Map, ast *d2ast.Map) error {
|
func Apply(dst *Map, ast *d2ast.Map) error {
|
||||||
var c compiler
|
var c compiler
|
||||||
c.apply(dst, ast)
|
c.compileMap(dst, ast)
|
||||||
if !c.err.Empty() {
|
if !c.err.Empty() {
|
||||||
return c.err
|
return c.err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compiler) apply(dst *Map, ast *d2ast.Map) {
|
func (c *compiler) compileMap(dst *Map, ast *d2ast.Map) {
|
||||||
for _, n := range ast.Nodes {
|
for _, n := range ast.Nodes {
|
||||||
if n.MapKey == nil {
|
switch {
|
||||||
continue
|
case n.MapKey != nil:
|
||||||
|
c.compileField(dst, n.MapKey)
|
||||||
|
case n.Substitution != nil:
|
||||||
|
panic("TODO")
|
||||||
}
|
}
|
||||||
|
|
||||||
c.applyKey(dst, n.MapKey)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compiler) applyKey(dst *Map, k *d2ast.Key) {
|
func (c *compiler) compileField(dst *Map, k *d2ast.Key) {
|
||||||
if k.Key != nil && len(k.Key.Path) > 0 {
|
if k.Key != nil && len(k.Key.Path) > 0 {
|
||||||
f, ok := dst.Ensure(d2graph.Key(k.Key))
|
f, ok := dst.Ensure(d2format.KeyPath(k.Key))
|
||||||
if !ok {
|
if !ok {
|
||||||
c.errorf(k.Key, "cannot index into array")
|
c.errorf(k, "cannot index into array")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,6 +56,28 @@ func (c *compiler) applyKey(dst *Map, k *d2ast.Key) {
|
||||||
Value: k.Primary.Unbox(),
|
Value: k.Primary.Unbox(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if k.Value.Array != nil {
|
||||||
|
a := &Array{
|
||||||
|
parent: f,
|
||||||
|
}
|
||||||
|
c.compileArray(a, k.Value.Array)
|
||||||
|
f.Composite = a
|
||||||
|
} else if k.Value.Map != nil {
|
||||||
|
m := &Map{
|
||||||
|
parent: f,
|
||||||
|
}
|
||||||
|
c.compileMap(m, k.Value.Map)
|
||||||
|
f.Composite = m
|
||||||
|
} else if k.Value.ScalarBox().Unbox() != nil {
|
||||||
|
f.Primary = &Scalar{
|
||||||
|
parent: f,
|
||||||
|
Value: k.Value.ScalarBox().Unbox(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *compiler) compileArray(dst *Array, a *d2ast.Array) {
|
||||||
|
panic(fmt.Sprintf("TODO"))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"oss.terrastruct.com/util-go/diff"
|
"oss.terrastruct.com/util-go/diff"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/d2ast"
|
"oss.terrastruct.com/d2/d2ast"
|
||||||
|
"oss.terrastruct.com/d2/d2format"
|
||||||
"oss.terrastruct.com/d2/d2ir"
|
"oss.terrastruct.com/d2/d2ir"
|
||||||
"oss.terrastruct.com/d2/d2parser"
|
"oss.terrastruct.com/d2/d2parser"
|
||||||
)
|
)
|
||||||
|
|
@ -31,7 +32,7 @@ func testApplySimple(t *testing.T) {
|
||||||
|
|
||||||
tca := []testCase{
|
tca := []testCase{
|
||||||
{
|
{
|
||||||
name: "one",
|
name: "field",
|
||||||
run: func(t testing.TB, m *d2ir.Map) {
|
run: func(t testing.TB, m *d2ir.Map) {
|
||||||
err := parse(t, m, `x`)
|
err := parse(t, m, `x`)
|
||||||
assert.Success(t, err)
|
assert.Success(t, err)
|
||||||
|
|
@ -40,6 +41,62 @@ func testApplySimple(t *testing.T) {
|
||||||
assertField(t, m, 0, 0, nil, "x")
|
assertField(t, m, 0, 0, nil, "x")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "field/label",
|
||||||
|
run: func(t testing.TB, m *d2ir.Map) {
|
||||||
|
err := parse(t, m, `x: yes`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertField(t, m, 1, 0, nil)
|
||||||
|
|
||||||
|
assertField(t, m, 0, 0, "yes", "x")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "field/label/nested",
|
||||||
|
run: func(t testing.TB, m *d2ir.Map) {
|
||||||
|
err := parse(t, m, `x.y: yes`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertField(t, m, 2, 0, nil)
|
||||||
|
|
||||||
|
assertField(t, m, 1, 0, nil, "x")
|
||||||
|
assertField(t, m, 0, 0, "yes", "x", "y")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "primary",
|
||||||
|
run: func(t testing.TB, m *d2ir.Map) {
|
||||||
|
err := parse(t, m, `x: yes { pqrs }`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertField(t, m, 2, 0, nil)
|
||||||
|
|
||||||
|
assertField(t, m, 1, 0, "yes", "x")
|
||||||
|
assertField(t, m, 0, 0, nil, "x", "pqrs")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "primary/nested",
|
||||||
|
run: func(t testing.TB, m *d2ir.Map) {
|
||||||
|
err := parse(t, m, `x.y: yes { pqrs }`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertField(t, m, 3, 0, nil)
|
||||||
|
|
||||||
|
assertField(t, m, 2, 0, nil, "x")
|
||||||
|
assertField(t, m, 1, 0, "yes", "x", "y")
|
||||||
|
assertField(t, m, 0, 0, nil, "x", "y", "pqrs")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "edge",
|
||||||
|
run: func(t testing.TB, m *d2ir.Map) {
|
||||||
|
err := parse(t, m, `x -> y`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertField(t, m, 2, 1, nil)
|
||||||
|
assertEdge(t, m, 0, nil, `(x -> y)[0]`)
|
||||||
|
|
||||||
|
assertField(t, m, 0, 0, nil, "x")
|
||||||
|
assertField(t, m, 0, 0, nil, "y")
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "nested",
|
name: "nested",
|
||||||
run: func(t testing.TB, m *d2ir.Map) {
|
run: func(t testing.TB, m *d2ir.Map) {
|
||||||
|
|
@ -53,11 +110,7 @@ func testApplySimple(t *testing.T) {
|
||||||
assertField(t, m, 1, 0, nil, "z")
|
assertField(t, m, 1, 0, nil, "z")
|
||||||
assertField(t, m, 0, 0, nil, "z", "p")
|
assertField(t, m, 0, 0, nil, "z", "p")
|
||||||
|
|
||||||
assertEdge(t, m, 0, nil, &d2ir.EdgeID{
|
assertEdge(t, m, 0, nil, "(x.y -> z.p)[0]")
|
||||||
[]string{"x", "y"}, false,
|
|
||||||
[]string{"z", "p"}, true,
|
|
||||||
-1,
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -70,11 +123,7 @@ func testApplySimple(t *testing.T) {
|
||||||
assertField(t, m, 0, 0, nil, "x")
|
assertField(t, m, 0, 0, nil, "x")
|
||||||
assertField(t, m, 0, 0, nil, "z")
|
assertField(t, m, 0, 0, nil, "z")
|
||||||
|
|
||||||
assertEdge(t, m, 0, nil, &d2ir.EdgeID{
|
assertEdge(t, m, 0, nil, "(x -> z)[0]")
|
||||||
[]string{"x"}, false,
|
|
||||||
[]string{"z"}, true,
|
|
||||||
-1,
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -150,14 +199,30 @@ func assertField(t testing.TB, n d2ir.Node, nfields, nedges int, primary interfa
|
||||||
assert.Equal(t, nfields, m.FieldCount())
|
assert.Equal(t, nfields, m.FieldCount())
|
||||||
assert.Equal(t, nedges, m.EdgeCount())
|
assert.Equal(t, nedges, m.EdgeCount())
|
||||||
if !makeScalar(p).Equal(makeScalar(primary)) {
|
if !makeScalar(p).Equal(makeScalar(primary)) {
|
||||||
t.Fatalf("expected primary %#v but %#v", primary, p)
|
t.Fatalf("expected primary %#v but got %s", primary, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertEdge(t testing.TB, n d2ir.Node, nfields int, primary interface{}, eid *d2ir.EdgeID) *d2ir.Edge {
|
func parseEdgeID(t testing.TB, eids string) *d2ir.EdgeID {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
k, err := d2parser.ParseMapKey(eids)
|
||||||
|
assert.Success(t, err)
|
||||||
|
|
||||||
|
return &d2ir.EdgeID{
|
||||||
|
SrcPath: d2format.KeyPath(k.Edges[0].Src),
|
||||||
|
SrcArrow: k.Edges[0].SrcArrow == "<",
|
||||||
|
DstPath: d2format.KeyPath(k.Edges[0].Dst),
|
||||||
|
DstArrow: k.Edges[0].DstArrow == ">",
|
||||||
|
Index: *k.EdgeIndex.Int,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertEdge(t testing.TB, n d2ir.Node, nfields int, primary interface{}, eids string) *d2ir.Edge {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
eid := parseEdgeID(t, eids)
|
||||||
|
|
||||||
var m *d2ir.Map
|
var m *d2ir.Map
|
||||||
switch n := n.(type) {
|
switch n := n.(type) {
|
||||||
|
|
@ -181,7 +246,7 @@ func assertEdge(t testing.TB, n d2ir.Node, nfields int, primary interface{}, eid
|
||||||
|
|
||||||
assert.Equal(t, nfields, e.Map.FieldCount())
|
assert.Equal(t, nfields, e.Map.FieldCount())
|
||||||
if !makeScalar(e.Primary).Equal(makeScalar(primary)) {
|
if !makeScalar(e.Primary).Equal(makeScalar(primary)) {
|
||||||
t.Fatalf("expected primary %#v but %#v", primary, e.Primary)
|
t.Fatalf("expected primary %#v but %s", primary, e.Primary)
|
||||||
}
|
}
|
||||||
|
|
||||||
return e
|
return e
|
||||||
|
|
|
||||||
21
d2ir/d2ir.go
21
d2ir/d2ir.go
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/d2ast"
|
"oss.terrastruct.com/d2/d2ast"
|
||||||
|
"oss.terrastruct.com/d2/d2format"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Node interface {
|
type Node interface {
|
||||||
|
|
@ -54,10 +55,10 @@ func (n *Array) node() {}
|
||||||
func (n *Map) node() {}
|
func (n *Map) node() {}
|
||||||
|
|
||||||
func (n *Scalar) Parent() Parent { return n.parent }
|
func (n *Scalar) Parent() Parent { return n.parent }
|
||||||
func (n *Field) Parent() Parent { return n.parent }
|
func (n *Field) Parent() Parent { return n.parent }
|
||||||
func (n *Edge) Parent() Parent { return n.parent }
|
func (n *Edge) Parent() Parent { return n.parent }
|
||||||
func (n *Array) Parent() Parent { return n.parent }
|
func (n *Array) Parent() Parent { return n.parent }
|
||||||
func (n *Map) Parent() Parent { return n.parent }
|
func (n *Map) Parent() Parent { return n.parent }
|
||||||
|
|
||||||
func (n *Scalar) value() {}
|
func (n *Scalar) value() {}
|
||||||
func (n *Array) value() {}
|
func (n *Array) value() {}
|
||||||
|
|
@ -80,7 +81,17 @@ func (s *Scalar) Copy(newp Parent) Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scalar) Equal(s2 *Scalar) bool {
|
func (s *Scalar) Equal(s2 *Scalar) bool {
|
||||||
return s.Value.ScalarString() == s2.Value.ScalarString() && s.Value.Type() == s2.Value.Type()
|
if _, ok := s.Value.(d2ast.String); ok {
|
||||||
|
if _, ok = s2.Value.(d2ast.String); ok {
|
||||||
|
return s.Value.ScalarString() == s2.Value.ScalarString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s.Value.Type() == s2.Value.Type() && s.Value.ScalarString() == s2.Value.ScalarString()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scalar) String() string {
|
||||||
|
return d2format.Format(s.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Map struct {
|
type Map struct {
|
||||||
|
|
|
||||||
4
testdata/d2ir/TestApply/simple/edge.exp.json
generated
vendored
Normal file
4
testdata/d2ir/TestApply/simple/edge.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"fields": null,
|
||||||
|
"edges": null
|
||||||
|
}
|
||||||
8
testdata/d2ir/TestApply/simple/field.exp.json
generated
vendored
Normal file
8
testdata/d2ir/TestApply/simple/field.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": null
|
||||||
|
}
|
||||||
19
testdata/d2ir/TestApply/simple/field/label.exp.json
generated
vendored
Normal file
19
testdata/d2ir/TestApply/simple/field/label.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "x",
|
||||||
|
"primary": {
|
||||||
|
"value": {
|
||||||
|
"range": "d2/testdata/d2ir/TestApply/simple/field/label.d2,0:3:3-0:6:6",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"string": "yes",
|
||||||
|
"raw_string": "yes"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": null
|
||||||
|
}
|
||||||
27
testdata/d2ir/TestApply/simple/field/label/nested.exp.json
generated
vendored
Normal file
27
testdata/d2ir/TestApply/simple/field/label/nested.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "x",
|
||||||
|
"composite": {
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "y",
|
||||||
|
"primary": {
|
||||||
|
"value": {
|
||||||
|
"range": "d2/testdata/d2ir/TestApply/simple/field/label/nested.d2,0:5:5-0:8:8",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"string": "yes",
|
||||||
|
"raw_string": "yes"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": null
|
||||||
|
}
|
||||||
19
testdata/d2ir/TestApply/simple/label.exp.json
generated
vendored
Normal file
19
testdata/d2ir/TestApply/simple/label.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "x",
|
||||||
|
"primary": {
|
||||||
|
"value": {
|
||||||
|
"range": "d2/testdata/d2ir/TestApply/simple/label.d2,0:3:3-0:6:6",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"string": "yes",
|
||||||
|
"raw_string": "yes"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": null
|
||||||
|
}
|
||||||
27
testdata/d2ir/TestApply/simple/primary#01.exp.json
generated
vendored
Normal file
27
testdata/d2ir/TestApply/simple/primary#01.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "x",
|
||||||
|
"primary": {
|
||||||
|
"value": {
|
||||||
|
"range": "d2/testdata/d2ir/TestApply/simple/primary#01.d2,0:3:3-0:6:6",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"string": "yes",
|
||||||
|
"raw_string": "yes"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"composite": {
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "pqrs"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": null
|
||||||
|
}
|
||||||
27
testdata/d2ir/TestApply/simple/primary.exp.json
generated
vendored
Normal file
27
testdata/d2ir/TestApply/simple/primary.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "x",
|
||||||
|
"primary": {
|
||||||
|
"value": {
|
||||||
|
"range": "d2/testdata/d2ir/TestApply/simple/primary.d2,0:3:3-0:6:6",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"string": "yes",
|
||||||
|
"raw_string": "yes"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"composite": {
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "pqrs"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": null
|
||||||
|
}
|
||||||
35
testdata/d2ir/TestApply/simple/primary/nested.exp.json
generated
vendored
Normal file
35
testdata/d2ir/TestApply/simple/primary/nested.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "x",
|
||||||
|
"composite": {
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "y",
|
||||||
|
"primary": {
|
||||||
|
"value": {
|
||||||
|
"range": "d2/testdata/d2ir/TestApply/simple/primary/nested.d2,0:5:5-0:8:8",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"string": "yes",
|
||||||
|
"raw_string": "yes"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"composite": {
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "pqrs"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": null
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue