mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 09:22:43 +03:00
console: add remoteSchemaTree component
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/3643 Co-authored-by: Matt Hardman <28978422+mattshardman@users.noreply.github.com> GitOrigin-RevId: 4797a3167f308ea8a7b144c7e12b3273217ebe3a
This commit is contained in:
parent
20b2a13f22
commit
62aaa7f1e3
536
console/package-lock.json
generated
536
console/package-lock.json
generated
@ -4,6 +4,43 @@
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@ant-design/colors": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-6.0.0.tgz",
|
||||
"integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==",
|
||||
"requires": {
|
||||
"@ctrl/tinycolor": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"@ant-design/icons": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.7.0.tgz",
|
||||
"integrity": "sha512-aoB4Z7JA431rt6d4u+8xcNPPCrdufSRMUOpxa1ab6mz1JCQZOEVolj2WVs/tDFmN62zzK30mNelEsprLYsSF3g==",
|
||||
"requires": {
|
||||
"@ant-design/colors": "^6.0.0",
|
||||
"@ant-design/icons-svg": "^4.2.1",
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"classnames": "^2.2.6",
|
||||
"rc-util": "^5.9.4"
|
||||
}
|
||||
},
|
||||
"@ant-design/icons-svg": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.2.1.tgz",
|
||||
"integrity": "sha512-EB0iwlKDGpG93hW8f85CTJTs4SvMX7tt5ceupvhALp1IF44SeUFOMhKUOYqpsoYWQKAOuTRDMqn75rEaKDp0Xw=="
|
||||
},
|
||||
"@ant-design/react-slick": {
|
||||
"version": "0.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-0.28.4.tgz",
|
||||
"integrity": "sha512-j9eAHTn7GxbXUFNknJoHS2ceAsqrQi2j8XykjZE1IXCD8kJF+t28EvhBLniDpbOsBk/3kjalnhriTfZcjBHNqg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.4",
|
||||
"classnames": "^2.2.5",
|
||||
"json2mq": "^0.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"resize-observer-polyfill": "^1.5.0"
|
||||
}
|
||||
},
|
||||
"@ardatan/aggregate-error": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz",
|
||||
@ -2677,6 +2714,11 @@
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"@ctrl/tinycolor": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz",
|
||||
"integrity": "sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ=="
|
||||
},
|
||||
"@cypress/request": {
|
||||
"version": "2.88.5",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.5.tgz",
|
||||
@ -22832,6 +22874,62 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"antd": {
|
||||
"version": "4.18.6",
|
||||
"resolved": "https://registry.npmjs.org/antd/-/antd-4.18.6.tgz",
|
||||
"integrity": "sha512-CgliJK1iFUYVER2zJsBUManwxLKhdrRVdhpYeHByglZ8w/WXmgIJ3xoSwwM0o4A/FA+gRuDa0f7kVnzPDiqdYw==",
|
||||
"requires": {
|
||||
"@ant-design/colors": "^6.0.0",
|
||||
"@ant-design/icons": "^4.7.0",
|
||||
"@ant-design/react-slick": "~0.28.1",
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@ctrl/tinycolor": "^3.4.0",
|
||||
"classnames": "^2.2.6",
|
||||
"copy-to-clipboard": "^3.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"memoize-one": "^6.0.0",
|
||||
"moment": "^2.25.3",
|
||||
"rc-cascader": "~3.2.1",
|
||||
"rc-checkbox": "~2.3.0",
|
||||
"rc-collapse": "~3.1.0",
|
||||
"rc-dialog": "~8.6.0",
|
||||
"rc-drawer": "~4.4.2",
|
||||
"rc-dropdown": "~3.2.5",
|
||||
"rc-field-form": "~1.22.0-2",
|
||||
"rc-image": "~5.2.5",
|
||||
"rc-input-number": "~7.3.0",
|
||||
"rc-mentions": "~1.6.1",
|
||||
"rc-menu": "~9.2.1",
|
||||
"rc-motion": "^2.4.4",
|
||||
"rc-notification": "~4.5.7",
|
||||
"rc-pagination": "~3.1.9",
|
||||
"rc-picker": "~2.5.17",
|
||||
"rc-progress": "~3.2.1",
|
||||
"rc-rate": "~2.9.0",
|
||||
"rc-resize-observer": "^1.2.0",
|
||||
"rc-select": "~14.0.0-alpha.15",
|
||||
"rc-slider": "~9.7.4",
|
||||
"rc-steps": "~4.1.0",
|
||||
"rc-switch": "~3.2.0",
|
||||
"rc-table": "~7.22.2",
|
||||
"rc-tabs": "~11.10.0",
|
||||
"rc-textarea": "~0.3.0",
|
||||
"rc-tooltip": "~5.1.1",
|
||||
"rc-tree": "~5.4.3",
|
||||
"rc-tree-select": "~5.1.1",
|
||||
"rc-trigger": "^5.2.10",
|
||||
"rc-upload": "~4.3.0",
|
||||
"rc-util": "^5.14.0",
|
||||
"scroll-into-view-if-needed": "^2.2.25"
|
||||
},
|
||||
"dependencies": {
|
||||
"memoize-one": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"any-observable": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz",
|
||||
@ -23057,6 +23155,11 @@
|
||||
"is-string": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"array-tree-filter": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/array-tree-filter/-/array-tree-filter-2.1.0.tgz",
|
||||
"integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw=="
|
||||
},
|
||||
"array-union": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
|
||||
@ -23414,6 +23517,11 @@
|
||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
|
||||
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
|
||||
},
|
||||
"async-validator": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.0.7.tgz",
|
||||
"integrity": "sha512-Pj2IR7u8hmUEDOwB++su6baaRi+QvsgajuFB9j95foM1N2gy5HM4z60hfusIO0fBPG5uLAEl6yCJr1jNSVugEQ=="
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
@ -25701,8 +25809,7 @@
|
||||
"compute-scroll-into-view": {
|
||||
"version": "1.0.17",
|
||||
"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz",
|
||||
"integrity": "sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg=="
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
@ -27072,14 +27179,12 @@
|
||||
"date-fns": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz",
|
||||
"integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ=="
|
||||
},
|
||||
"dayjs": {
|
||||
"version": "1.10.5",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.5.tgz",
|
||||
"integrity": "sha512-BUFis41ikLz+65iH6LHQCDm4YPMj5r1YFLdupPIyM4SGcXMmtiLQ7U37i+hGS8urIuqe7I/ou3IS1jVc4nbN4g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-BUFis41ikLz+65iH6LHQCDm4YPMj5r1YFLdupPIyM4SGcXMmtiLQ7U37i+hGS8urIuqe7I/ou3IS1jVc4nbN4g=="
|
||||
},
|
||||
"debounce": {
|
||||
"version": "1.2.0",
|
||||
@ -27378,6 +27483,11 @@
|
||||
"integrity": "sha512-DplGLZd8L1lN64jlT27N9TVSESFR5STaEJvX+thCby7fuCHonfPpAlodYc3vuUYbDuDec5w8AMP7oCM5TWFsqw==",
|
||||
"dev": true
|
||||
},
|
||||
"dom-align": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.2.tgz",
|
||||
"integrity": "sha512-pHuazgqrsTFrGU2WLDdXxCFabkdQDx72ddkraZNih1KsMcN5qsRSTR9O4VJRlwTPCPb5COYg3LOfiMHHcPInHg=="
|
||||
},
|
||||
"dom-converter": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
|
||||
@ -34665,6 +34775,14 @@
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
||||
},
|
||||
"json2mq": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz",
|
||||
"integrity": "sha1-tje9O6nqvhIsg+lyBIOusQ0skEo=",
|
||||
"requires": {
|
||||
"string-convert": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"json5": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
|
||||
@ -40398,6 +40516,394 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"rc-align": {
|
||||
"version": "4.0.11",
|
||||
"resolved": "https://registry.npmjs.org/rc-align/-/rc-align-4.0.11.tgz",
|
||||
"integrity": "sha512-n9mQfIYQbbNTbefyQnRHZPWuTEwG1rY4a9yKlIWHSTbgwI+XUMGRYd0uJ5pE2UbrNX0WvnMBA1zJ3Lrecpra/A==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "2.x",
|
||||
"dom-align": "^1.7.0",
|
||||
"lodash": "^4.17.21",
|
||||
"rc-util": "^5.3.0",
|
||||
"resize-observer-polyfill": "^1.5.1"
|
||||
}
|
||||
},
|
||||
"rc-cascader": {
|
||||
"version": "3.2.5",
|
||||
"resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.2.5.tgz",
|
||||
"integrity": "sha512-kGyDPkVnCPheNqjZ+YeACbyZ9q6pDP1SYdMzM7G3JfdbJj3lpCWZkil/14AExstsmas2eddEQ5WGZzxG9707TQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"array-tree-filter": "^2.1.0",
|
||||
"classnames": "^2.3.1",
|
||||
"rc-select": "~14.0.0-alpha.23",
|
||||
"rc-tree": "~5.4.3",
|
||||
"rc-util": "^5.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"classnames": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
|
||||
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"rc-checkbox": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-2.3.2.tgz",
|
||||
"integrity": "sha512-afVi1FYiGv1U0JlpNH/UaEXdh6WUJjcWokj/nUN2TgG80bfG+MDdbfHKlLcNNba94mbjy2/SXJ1HDgrOkXGAjg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"rc-collapse": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-3.1.2.tgz",
|
||||
"integrity": "sha512-HujcKq7mghk/gVKeI6EjzTbb8e19XUZpakrYazu1MblEZ3Hu3WBMSN4A3QmvbF6n1g7x6lUlZvsHZ5shABWYOQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "2.x",
|
||||
"rc-motion": "^2.3.4",
|
||||
"rc-util": "^5.2.1",
|
||||
"shallowequal": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"rc-dialog": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-8.6.0.tgz",
|
||||
"integrity": "sha512-GSbkfqjqxpZC5/zc+8H332+q5l/DKUhpQr0vdX2uDsxo5K0PhvaMEVjyoJUTkZ3+JstEADQji1PVLVb/2bJeOQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.6",
|
||||
"rc-motion": "^2.3.0",
|
||||
"rc-util": "^5.6.1"
|
||||
}
|
||||
},
|
||||
"rc-drawer": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-4.4.3.tgz",
|
||||
"integrity": "sha512-FYztwRs3uXnFOIf1hLvFxIQP9MiZJA+0w+Os8dfDh/90X7z/HqP/Yg+noLCIeHEbKln1Tqelv8ymCAN24zPcfQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.6",
|
||||
"rc-util": "^5.7.0"
|
||||
}
|
||||
},
|
||||
"rc-dropdown": {
|
||||
"version": "3.2.5",
|
||||
"resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-3.2.5.tgz",
|
||||
"integrity": "sha512-dVO2eulOSbEf+F4OyhCY5iGiMVhUYY/qeXxL7Ex2jDBt/xc89jU07mNoowV6aWxwVOc70pxEINff0oM2ogjluA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.6",
|
||||
"rc-trigger": "^5.0.4"
|
||||
}
|
||||
},
|
||||
"rc-field-form": {
|
||||
"version": "1.22.1",
|
||||
"resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-1.22.1.tgz",
|
||||
"integrity": "sha512-LweU7nBeqmC5r3HDUjRprcOXXobHXp/TGIxD7ppBq5FX6Iptt3ibdpRVg4RSyNulBNGHOuknHlRcguuIpvVMVg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.8.4",
|
||||
"async-validator": "^4.0.2",
|
||||
"rc-util": "^5.8.0"
|
||||
}
|
||||
},
|
||||
"rc-image": {
|
||||
"version": "5.2.5",
|
||||
"resolved": "https://registry.npmjs.org/rc-image/-/rc-image-5.2.5.tgz",
|
||||
"integrity": "sha512-qUfZjYIODxO0c8a8P5GeuclYXZjzW4hV/5hyo27XqSFo1DmTCs2HkVeQObkcIk5kNsJtgsj1KoPThVsSc/PXOw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"classnames": "^2.2.6",
|
||||
"rc-dialog": "~8.6.0",
|
||||
"rc-util": "^5.0.6"
|
||||
}
|
||||
},
|
||||
"rc-input-number": {
|
||||
"version": "7.3.4",
|
||||
"resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-7.3.4.tgz",
|
||||
"integrity": "sha512-W9uqSzuvJUnz8H8vsVY4kx+yK51SsAxNTwr8SNH4G3XqQNocLVmKIibKFRjocnYX1RDHMND9FFbgj2h7E7nvGA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.5",
|
||||
"rc-util": "^5.9.8"
|
||||
}
|
||||
},
|
||||
"rc-mentions": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-1.6.1.tgz",
|
||||
"integrity": "sha512-LDzGI8jJVGnkhpTZxZuYBhMz3avcZZqPGejikchh97xPni/g4ht714Flh7DVvuzHQ+BoKHhIjobHnw1rcP8erg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.6",
|
||||
"rc-menu": "^9.0.0",
|
||||
"rc-textarea": "^0.3.0",
|
||||
"rc-trigger": "^5.0.4",
|
||||
"rc-util": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"rc-menu": {
|
||||
"version": "9.2.1",
|
||||
"resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.2.1.tgz",
|
||||
"integrity": "sha512-UbEtn3rflJ8zS+etYGTVQuzy7Fm+yWXR5c0Rl6ecNTS/dPknRyWAyhJcbeR0Hu1+RdQT+0VCqrUPrgKnm4iY+w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "2.x",
|
||||
"rc-motion": "^2.4.3",
|
||||
"rc-overflow": "^1.2.0",
|
||||
"rc-trigger": "^5.1.2",
|
||||
"rc-util": "^5.12.0",
|
||||
"shallowequal": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"rc-motion": {
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.4.4.tgz",
|
||||
"integrity": "sha512-ms7n1+/TZQBS0Ydd2Q5P4+wJTSOrhIrwNxLXCZpR7Fa3/oac7Yi803HDALc2hLAKaCTQtw9LmQeB58zcwOsqlQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.11.1",
|
||||
"classnames": "^2.2.1",
|
||||
"rc-util": "^5.2.1"
|
||||
}
|
||||
},
|
||||
"rc-notification": {
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-4.5.7.tgz",
|
||||
"integrity": "sha512-zhTGUjBIItbx96SiRu3KVURcLOydLUHZCPpYEn1zvh+re//Tnq/wSxN4FKgp38n4HOgHSVxcLEeSxBMTeBBDdw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "2.x",
|
||||
"rc-motion": "^2.2.0",
|
||||
"rc-util": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"rc-overflow": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.2.3.tgz",
|
||||
"integrity": "sha512-Bz6dXTn/ww8nmu70tUQfRV0wT3BkfXY6j1lB1O38OVkDPz4xwfAcGK+LJ2zewUR5cTXkJ8hAN7YULohG8z4M7Q==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.11.1",
|
||||
"classnames": "^2.2.1",
|
||||
"rc-resize-observer": "^1.0.0",
|
||||
"rc-util": "^5.15.0"
|
||||
}
|
||||
},
|
||||
"rc-pagination": {
|
||||
"version": "3.1.15",
|
||||
"resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-3.1.15.tgz",
|
||||
"integrity": "sha512-4L3fot8g4E+PjWEgoVGX0noFCg+8ZFZmeLH4vsnZpB3O2T2zThtakjNxG+YvSaYtyMVT4B+GLayjKrKbXQpdAg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"rc-picker": {
|
||||
"version": "2.5.19",
|
||||
"resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-2.5.19.tgz",
|
||||
"integrity": "sha512-u6myoCu/qiQ0vLbNzSzNrzTQhs7mldArCpPHrEI6OUiifs+IPXmbesqSm0zilJjfzrZJLgYeyyOMSznSlh0GKA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.1",
|
||||
"date-fns": "2.x",
|
||||
"dayjs": "1.x",
|
||||
"moment": "^2.24.0",
|
||||
"rc-trigger": "^5.0.4",
|
||||
"rc-util": "^5.4.0",
|
||||
"shallowequal": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"rc-progress": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-3.2.4.tgz",
|
||||
"integrity": "sha512-M9WWutRaoVkPUPIrTpRIDpX0SPSrVHzxHdCRCbeoBFrd9UFWTYNWRlHsruJM5FH1AZI+BwB4wOJUNNylg/uFSw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.6",
|
||||
"rc-util": "^5.16.1"
|
||||
}
|
||||
},
|
||||
"rc-rate": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.9.1.tgz",
|
||||
"integrity": "sha512-MmIU7FT8W4LYRRHJD1sgG366qKtSaKb67D0/vVvJYR0lrCuRrCiVQ5qhfT5ghVO4wuVIORGpZs7ZKaYu+KMUzA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.5",
|
||||
"rc-util": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"rc-resize-observer": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.2.0.tgz",
|
||||
"integrity": "sha512-6W+UzT3PyDM0wVCEHfoW3qTHPTvbdSgiA43buiy8PzmeMnfgnDeb9NjdimMXMl3/TcrvvWl5RRVdp+NqcR47pQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.1",
|
||||
"rc-util": "^5.15.0",
|
||||
"resize-observer-polyfill": "^1.5.1"
|
||||
}
|
||||
},
|
||||
"rc-select": {
|
||||
"version": "14.0.0-alpha.25",
|
||||
"resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.0.0-alpha.25.tgz",
|
||||
"integrity": "sha512-U9AMzXsOCCdtn96YIZdUrYbxk+5u6uWUCaYH2129X3FTjQITqAjEPYHfPcxU/G7+lwiD0pIaU95W0NMkg+26qw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "2.x",
|
||||
"rc-motion": "^2.0.1",
|
||||
"rc-overflow": "^1.0.0",
|
||||
"rc-trigger": "^5.0.4",
|
||||
"rc-util": "^5.16.1",
|
||||
"rc-virtual-list": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"rc-slider": {
|
||||
"version": "9.7.5",
|
||||
"resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-9.7.5.tgz",
|
||||
"integrity": "sha512-LV/MWcXFjco1epPbdw1JlLXlTgmWpB9/Y/P2yinf8Pg3wElHxA9uajN21lJiWtZjf5SCUekfSP6QMJfDo4t1hg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.5",
|
||||
"rc-tooltip": "^5.0.1",
|
||||
"rc-util": "^5.16.1",
|
||||
"shallowequal": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"rc-steps": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-4.1.4.tgz",
|
||||
"integrity": "sha512-qoCqKZWSpkh/b03ASGx1WhpKnuZcRWmvuW+ZUu4mvMdfvFzVxblTwUM+9aBd0mlEUFmt6GW8FXhMpHkK3Uzp3w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.2",
|
||||
"classnames": "^2.2.3",
|
||||
"rc-util": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"rc-switch": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-3.2.2.tgz",
|
||||
"integrity": "sha512-+gUJClsZZzvAHGy1vZfnwySxj+MjLlGRyXKXScrtCTcmiYNPzxDFOxdQ/3pK1Kt/0POvwJ/6ALOR8gwdXGhs+A==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.1",
|
||||
"rc-util": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"rc-table": {
|
||||
"version": "7.22.2",
|
||||
"resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.22.2.tgz",
|
||||
"integrity": "sha512-Ng2gNkGi6ybl6dzneRn2H4Gp8XhIbRa5rXQ7ZhZcgWVmfVMok70UHGPXcf68tXW6O0/qckTf/eOVsoviSvK4sw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.5",
|
||||
"rc-resize-observer": "^1.1.0",
|
||||
"rc-util": "^5.14.0",
|
||||
"shallowequal": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"rc-tabs": {
|
||||
"version": "11.10.5",
|
||||
"resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-11.10.5.tgz",
|
||||
"integrity": "sha512-DDuUdV6b9zGRYLtjI5hyejWLKoz1QiLWNgMeBzc3aMeQylZFhTYnFGdDc6HRqj5IYearNTsFPVSA+6VIT8g5cg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"classnames": "2.x",
|
||||
"rc-dropdown": "^3.2.0",
|
||||
"rc-menu": "^9.0.0",
|
||||
"rc-resize-observer": "^1.0.0",
|
||||
"rc-util": "^5.5.0"
|
||||
}
|
||||
},
|
||||
"rc-textarea": {
|
||||
"version": "0.3.7",
|
||||
"resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-0.3.7.tgz",
|
||||
"integrity": "sha512-yCdZ6binKmAQB13hc/oehh0E/QRwoPP1pjF21aHBxlgXO3RzPF6dUu4LG2R4FZ1zx/fQd2L1faktulrXOM/2rw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.1",
|
||||
"rc-resize-observer": "^1.0.0",
|
||||
"rc-util": "^5.7.0",
|
||||
"shallowequal": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"rc-tooltip": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-5.1.1.tgz",
|
||||
"integrity": "sha512-alt8eGMJulio6+4/uDm7nvV+rJq9bsfxFDCI0ljPdbuoygUscbsMYb6EQgwib/uqsXQUvzk+S7A59uYHmEgmDA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"rc-trigger": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"rc-tree": {
|
||||
"version": "5.4.3",
|
||||
"resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-5.4.3.tgz",
|
||||
"integrity": "sha512-WAHV8FkBerulj9J/+61+Qn0TD/Zo37PrDG8/45WomzGTYavxFMur9YguKjQj/J+NxjVJzrJL3lvdSZsumfdbiA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "2.x",
|
||||
"rc-motion": "^2.0.1",
|
||||
"rc-util": "^5.16.1",
|
||||
"rc-virtual-list": "^3.4.1"
|
||||
}
|
||||
},
|
||||
"rc-tree-select": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.1.2.tgz",
|
||||
"integrity": "sha512-sTulyQZB1SgF2HzAR49i4vjMNvV/tfPxCTc+qNuWOwaAtSm2bwGH6/wfi3k3Dw2/5PPOGpRRpgCMltoP9aG0+w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "2.x",
|
||||
"rc-select": "~14.0.0-alpha.8",
|
||||
"rc-tree": "~5.4.3",
|
||||
"rc-util": "^5.16.1"
|
||||
}
|
||||
},
|
||||
"rc-trigger": {
|
||||
"version": "5.2.10",
|
||||
"resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-5.2.10.tgz",
|
||||
"integrity": "sha512-FkUf4H9BOFDaIwu42fvRycXMAvkttph9AlbCZXssZDVzz2L+QZ0ERvfB/4nX3ZFPh1Zd+uVGr1DEDeXxq4J1TA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"classnames": "^2.2.6",
|
||||
"rc-align": "^4.0.0",
|
||||
"rc-motion": "^2.0.0",
|
||||
"rc-util": "^5.5.0"
|
||||
}
|
||||
},
|
||||
"rc-upload": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.3.3.tgz",
|
||||
"integrity": "sha512-YoJ0phCRenMj1nzwalXzciKZ9/FAaCrFu84dS5pphwucTC8GUWClcDID/WWNGsLFcM97NqIboDqrV82rVRhW/w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.5",
|
||||
"rc-util": "^5.2.0"
|
||||
}
|
||||
},
|
||||
"rc-util": {
|
||||
"version": "5.17.0",
|
||||
"resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.17.0.tgz",
|
||||
"integrity": "sha512-HWuTIKzBeZQQ7IBqdokE0wMp/xx39/KfUJ0gcquBigoldDCrf3YBcWFHrrQlJG7sI82Wg8mwp1uAKV3zMGfAgg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"react-is": "^16.12.0",
|
||||
"shallowequal": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"rc-virtual-list": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.4.2.tgz",
|
||||
"integrity": "sha512-OyVrrPvvFcHvV0ssz5EDZ+7Rf5qLat/+mmujjchNw5FfbJWNDwkpQ99EcVE6+FtNRmX9wFa1LGNpZLUTvp/4GQ==",
|
||||
"requires": {
|
||||
"classnames": "^2.2.6",
|
||||
"rc-resize-observer": "^1.0.0",
|
||||
"rc-util": "^5.0.7"
|
||||
}
|
||||
},
|
||||
"react": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz",
|
||||
@ -41881,6 +42387,11 @@
|
||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.0.0.tgz",
|
||||
"integrity": "sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA=="
|
||||
},
|
||||
"resize-observer-polyfill": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
|
||||
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz",
|
||||
@ -42441,6 +42952,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"scroll-into-view-if-needed": {
|
||||
"version": "2.2.29",
|
||||
"resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz",
|
||||
"integrity": "sha512-hxpAR6AN+Gh53AdAimHM6C8oTN1ppwVZITihix+WqalywBeFcQ6LdQP5ABNl26nX8GTEL7VT+b8lKpdqq65wXg==",
|
||||
"requires": {
|
||||
"compute-scroll-into-view": "^1.0.17"
|
||||
}
|
||||
},
|
||||
"scss-tokenizer": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
|
||||
@ -43216,6 +43735,11 @@
|
||||
"integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==",
|
||||
"dev": true
|
||||
},
|
||||
"string-convert": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz",
|
||||
"integrity": "sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c="
|
||||
},
|
||||
"string-length": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
|
||||
|
@ -66,6 +66,7 @@
|
||||
"@types/lodash.get": "^4.4.6",
|
||||
"@xstate/react": "^2.0.0",
|
||||
"ace-builds": "^1.4.11",
|
||||
"antd": "4.18.6",
|
||||
"apollo-link": "1.2.14",
|
||||
"apollo-link-ws": "1.0.20",
|
||||
"brace": "0.11.1",
|
||||
|
@ -0,0 +1,162 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import 'antd/dist/antd.css';
|
||||
import { Tree as AntTree } from 'antd';
|
||||
import { GraphQLSchema } from 'graphql';
|
||||
import { EventDataNode } from 'antd/lib/tree';
|
||||
import {
|
||||
AllowedRootFields,
|
||||
AntdTreeNode,
|
||||
HasuraColumn,
|
||||
RelationshipFields,
|
||||
TreeNode,
|
||||
} from './types';
|
||||
import {
|
||||
buildTree,
|
||||
findRemoteField,
|
||||
getFieldData,
|
||||
getExpandedKeys,
|
||||
getCheckedKeys,
|
||||
} from './utils';
|
||||
|
||||
export interface RemoteSchemaTreeProps {
|
||||
/**
|
||||
* Graphql schema for setting new permissions.
|
||||
*/
|
||||
schema: GraphQLSchema;
|
||||
relationshipFields: RelationshipFields[];
|
||||
rootFields: AllowedRootFields;
|
||||
setRelationshipFields: React.Dispatch<
|
||||
React.SetStateAction<RelationshipFields[]>
|
||||
>;
|
||||
/**
|
||||
* Columns array from the current table.
|
||||
*/
|
||||
columns: HasuraColumn;
|
||||
}
|
||||
|
||||
export const RemoteSchemaTree = ({
|
||||
schema,
|
||||
relationshipFields,
|
||||
rootFields,
|
||||
setRelationshipFields,
|
||||
columns,
|
||||
}: RemoteSchemaTreeProps) => {
|
||||
const tree: TreeNode[] = useMemo(
|
||||
() =>
|
||||
buildTree(
|
||||
schema,
|
||||
relationshipFields,
|
||||
setRelationshipFields,
|
||||
columns,
|
||||
rootFields
|
||||
),
|
||||
[relationshipFields, schema, rootFields, columns]
|
||||
);
|
||||
|
||||
const expandedKeys = useMemo(() => getExpandedKeys(relationshipFields), [
|
||||
relationshipFields,
|
||||
]);
|
||||
|
||||
const checkedKeys = useMemo(() => getCheckedKeys(relationshipFields), [
|
||||
relationshipFields,
|
||||
]);
|
||||
|
||||
const onCheck = (
|
||||
// onCheck props expects checked param
|
||||
checked:
|
||||
| React.Key[]
|
||||
| {
|
||||
checked: React.Key[];
|
||||
halfChecked: React.Key[];
|
||||
},
|
||||
// CheckInfo is not exported by the library
|
||||
// https://github.com/react-component/tree/issues/411
|
||||
checkedNodeInfo: Record<string, any>
|
||||
) => {
|
||||
const nodeInfo = checkedNodeInfo.node as AntdTreeNode;
|
||||
const selectedField = findRemoteField(relationshipFields, nodeInfo);
|
||||
const fieldData = getFieldData(nodeInfo);
|
||||
|
||||
if (selectedField) {
|
||||
setRelationshipFields(
|
||||
relationshipFields.filter(field => !(field.key === nodeInfo.key))
|
||||
);
|
||||
} else {
|
||||
setRelationshipFields([
|
||||
...relationshipFields.filter(field => !(field.key === nodeInfo.key)),
|
||||
fieldData,
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
const onExpand = (
|
||||
expanded: React.Key[],
|
||||
expandedNodeInfo: {
|
||||
node: EventDataNode;
|
||||
expanded: boolean;
|
||||
nativeEvent: MouseEvent;
|
||||
}
|
||||
) => {
|
||||
const nodeInfo = expandedNodeInfo.node as AntdTreeNode;
|
||||
const selectedField = findRemoteField(relationshipFields, nodeInfo);
|
||||
const fieldData = getFieldData(nodeInfo);
|
||||
if (selectedField) {
|
||||
// if the node is already expanded, collapse the node,
|
||||
// and remove all its children
|
||||
setRelationshipFields(
|
||||
relationshipFields.filter(
|
||||
field =>
|
||||
!(
|
||||
field.key === nodeInfo.key ||
|
||||
field.key.includes(`${nodeInfo.key}.`)
|
||||
)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// `fields` at same or higher depth, if the current node is `argument` we skip this
|
||||
const levelDepthFields =
|
||||
nodeInfo.type === 'field'
|
||||
? relationshipFields
|
||||
.filter(
|
||||
field => field.type === 'field' && field.depth >= nodeInfo.depth
|
||||
)
|
||||
.map(field => field.key)
|
||||
: [];
|
||||
|
||||
// remove all the fields and their children which are on same/higher depth, and add the current field
|
||||
// as one parent can have only one field at a certain depth
|
||||
setRelationshipFields([
|
||||
...relationshipFields.filter(
|
||||
field =>
|
||||
!(
|
||||
field.key === nodeInfo.key ||
|
||||
// remove all current or higher depth fields and their children
|
||||
(nodeInfo.type === 'field' &&
|
||||
levelDepthFields.some(
|
||||
refFieldKey =>
|
||||
field.key === refFieldKey ||
|
||||
field.key.includes(`${refFieldKey}.`)
|
||||
))
|
||||
)
|
||||
),
|
||||
fieldData,
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<AntTree
|
||||
checkable
|
||||
checkStrictly
|
||||
blockNode
|
||||
selectable={false}
|
||||
onCheck={onCheck}
|
||||
onExpand={onExpand}
|
||||
treeData={tree}
|
||||
expandedKeys={expandedKeys}
|
||||
checkedKeys={checkedKeys}
|
||||
// disable animation onExpand to improve performance
|
||||
motion={null}
|
||||
/>
|
||||
);
|
||||
};
|
@ -0,0 +1,52 @@
|
||||
import React from 'react';
|
||||
import { Story, Meta } from '@storybook/react';
|
||||
import { buildClientSchema } from 'graphql';
|
||||
import countries from './fixtures/countries.json';
|
||||
import hasura_schema from './fixtures/hasura_schema.json';
|
||||
import { customer_columns, remote_rel_definition } from './fixtures/constants';
|
||||
|
||||
import {
|
||||
RemoteSchemaTreeWrapper,
|
||||
RemoteSchemaTreeWrapperProps,
|
||||
} from './RemoteSchemaTreeWrapper';
|
||||
|
||||
export default {
|
||||
title: 'Remote Relationships/Components/Remote Schema Tree Wrapper',
|
||||
component: RemoteSchemaTreeWrapper,
|
||||
} as Meta;
|
||||
|
||||
export const RemoteSchemaTreeWithCountriesSchema: Story<RemoteSchemaTreeWrapperProps> = args => (
|
||||
<RemoteSchemaTreeWrapper {...args} />
|
||||
);
|
||||
RemoteSchemaTreeWithCountriesSchema.args = {
|
||||
schema: buildClientSchema(countries as any),
|
||||
columns: customer_columns,
|
||||
rootFields: ['query', 'mutation', 'subscription'],
|
||||
};
|
||||
RemoteSchemaTreeWithCountriesSchema.parameters = {
|
||||
// Disable chromatic snapshot for playground stories
|
||||
chromatic: { disableSnapshot: true },
|
||||
};
|
||||
|
||||
export const RemoteSchemaTreeWithHasuraCloudSchema: Story<RemoteSchemaTreeWrapperProps> = args => (
|
||||
<RemoteSchemaTreeWrapper {...args} />
|
||||
);
|
||||
RemoteSchemaTreeWithHasuraCloudSchema.args = {
|
||||
schema: buildClientSchema(hasura_schema as any),
|
||||
columns: customer_columns,
|
||||
rootFields: ['query', 'mutation'],
|
||||
};
|
||||
RemoteSchemaTreeWithHasuraCloudSchema.parameters = {
|
||||
// Disable chromatic snapshot for playground stories
|
||||
chromatic: { disableSnapshot: true },
|
||||
};
|
||||
|
||||
export const RemoteSchemaTreeWithExistingRelationship: Story<RemoteSchemaTreeWrapperProps> = args => (
|
||||
<RemoteSchemaTreeWrapper {...args} />
|
||||
);
|
||||
RemoteSchemaTreeWithExistingRelationship.args = {
|
||||
schema: buildClientSchema(hasura_schema as any),
|
||||
columns: customer_columns,
|
||||
rootFields: ['query', 'mutation'],
|
||||
serverRelationship: remote_rel_definition as any,
|
||||
};
|
@ -0,0 +1,71 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { GraphQLSchema } from 'graphql';
|
||||
import { RemoteRelationship } from '@/metadata/types';
|
||||
import { Button } from '@/new-components/Button';
|
||||
import { RemoteSchemaTree } from './RemoteSchemaTree';
|
||||
import { HasuraColumn, AllowedRootFields, RelationshipFields } from './types';
|
||||
import { buildServerRemoteFieldObject, parseServerRelationship } from './utils';
|
||||
|
||||
export interface RemoteSchemaTreeWrapperProps {
|
||||
/**
|
||||
* Graphql schema for setting new permissions.
|
||||
*/
|
||||
schema: GraphQLSchema;
|
||||
/**
|
||||
* Columns array from the current table.
|
||||
*/
|
||||
columns: HasuraColumn;
|
||||
/**
|
||||
* Remote relationship object from server, for already present permissions.
|
||||
* This will be parsed and tree will be populated accordingly
|
||||
*/
|
||||
serverRelationship?: RemoteRelationship;
|
||||
rootFields?: AllowedRootFields;
|
||||
}
|
||||
|
||||
export const RemoteSchemaTreeWrapper = ({
|
||||
schema,
|
||||
columns,
|
||||
serverRelationship,
|
||||
rootFields = ['query'],
|
||||
}: RemoteSchemaTreeWrapperProps) => {
|
||||
const [relationshipFields, setRelationshipFields] = useState<
|
||||
RelationshipFields[]
|
||||
>([]);
|
||||
|
||||
const [serverRelObject, setServerRelObject] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
if (serverRelationship) {
|
||||
setRelationshipFields(parseServerRelationship(serverRelationship));
|
||||
}
|
||||
}, [serverRelationship]);
|
||||
|
||||
const handleSave = () => {
|
||||
setServerRelObject(
|
||||
JSON.stringify(
|
||||
buildServerRemoteFieldObject(relationshipFields) ?? '',
|
||||
null,
|
||||
4
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RemoteSchemaTree
|
||||
schema={schema}
|
||||
relationshipFields={relationshipFields}
|
||||
setRelationshipFields={setRelationshipFields}
|
||||
columns={columns}
|
||||
rootFields={rootFields}
|
||||
/>
|
||||
<div className="mt-lg">
|
||||
<Button onClick={handleSave}>Generate Relationship Object</Button>
|
||||
</div>
|
||||
<div className="mt-lg">
|
||||
<code className="whitespace-pre-wrap">{serverRelObject}</code>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,371 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Utils.ts buildServerRemoteFieldObject 1`] = `
|
||||
Object {
|
||||
"arguments": Object {
|
||||
"where": Object {
|
||||
"id": Object {
|
||||
"_eq": "$id",
|
||||
},
|
||||
},
|
||||
},
|
||||
"field": Object {
|
||||
"aggregate": Object {
|
||||
"arguments": Object {},
|
||||
"field": Object {
|
||||
"count": Object {
|
||||
"arguments": Object {
|
||||
"distinct": "$id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Utils.ts buildTree 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"checkable": false,
|
||||
"children": Array [
|
||||
Object {
|
||||
"checkable": false,
|
||||
"children": Array [
|
||||
Object {
|
||||
"checkable": false,
|
||||
"depth": 2,
|
||||
"disabled": true,
|
||||
"key": "__query.field.testUser.__placeholder.1",
|
||||
"title": "",
|
||||
"type": "field",
|
||||
},
|
||||
],
|
||||
"depth": 1,
|
||||
"disabled": false,
|
||||
"key": "__query.field.testUser",
|
||||
"title": <SubFieldTitle
|
||||
enabled={true}
|
||||
isSubfield={false}
|
||||
title="testUser"
|
||||
/>,
|
||||
"type": "field",
|
||||
},
|
||||
Object {
|
||||
"checkable": false,
|
||||
"children": Array [
|
||||
Object {
|
||||
"checkable": false,
|
||||
"depth": 2,
|
||||
"disabled": true,
|
||||
"key": "__query.field.testUser_aggregate.__placeholder.1",
|
||||
"title": "",
|
||||
"type": "field",
|
||||
},
|
||||
],
|
||||
"depth": 1,
|
||||
"disabled": false,
|
||||
"key": "__query.field.testUser_aggregate",
|
||||
"title": <SubFieldTitle
|
||||
enabled={true}
|
||||
isSubfield={false}
|
||||
title="testUser_aggregate"
|
||||
/>,
|
||||
"type": "field",
|
||||
},
|
||||
Object {
|
||||
"checkable": false,
|
||||
"children": Array [
|
||||
Object {
|
||||
"checkable": false,
|
||||
"depth": 2,
|
||||
"disabled": true,
|
||||
"key": "__query.field.testUser_by_pk.__placeholder.1",
|
||||
"title": "",
|
||||
"type": "field",
|
||||
},
|
||||
],
|
||||
"depth": 1,
|
||||
"disabled": false,
|
||||
"key": "__query.field.testUser_by_pk",
|
||||
"title": <SubFieldTitle
|
||||
enabled={true}
|
||||
isSubfield={false}
|
||||
title="testUser_by_pk"
|
||||
/>,
|
||||
"type": "field",
|
||||
},
|
||||
],
|
||||
"depth": 0,
|
||||
"key": "__query",
|
||||
"title": <RootFieldTitle
|
||||
title="Query"
|
||||
/>,
|
||||
"type": "field",
|
||||
},
|
||||
Object {
|
||||
"checkable": false,
|
||||
"children": Array [
|
||||
Object {
|
||||
"checkable": false,
|
||||
"children": Array [
|
||||
Object {
|
||||
"checkable": false,
|
||||
"depth": 2,
|
||||
"disabled": true,
|
||||
"key": "__mutation.field.delete_testUser.__placeholder.1",
|
||||
"title": "",
|
||||
"type": "field",
|
||||
},
|
||||
],
|
||||
"depth": 1,
|
||||
"disabled": false,
|
||||
"key": "__mutation.field.delete_testUser",
|
||||
"title": <SubFieldTitle
|
||||
enabled={true}
|
||||
isSubfield={false}
|
||||
title="delete_testUser"
|
||||
/>,
|
||||
"type": "field",
|
||||
},
|
||||
Object {
|
||||
"checkable": false,
|
||||
"children": Array [
|
||||
Object {
|
||||
"checkable": false,
|
||||
"depth": 2,
|
||||
"disabled": true,
|
||||
"key": "__mutation.field.delete_testUser_by_pk.__placeholder.1",
|
||||
"title": "",
|
||||
"type": "field",
|
||||
},
|
||||
],
|
||||
"depth": 1,
|
||||
"disabled": false,
|
||||
"key": "__mutation.field.delete_testUser_by_pk",
|
||||
"title": <SubFieldTitle
|
||||
enabled={true}
|
||||
isSubfield={false}
|
||||
title="delete_testUser_by_pk"
|
||||
/>,
|
||||
"type": "field",
|
||||
},
|
||||
Object {
|
||||
"checkable": false,
|
||||
"children": Array [
|
||||
Object {
|
||||
"checkable": false,
|
||||
"depth": 2,
|
||||
"disabled": true,
|
||||
"key": "__mutation.field.insert_testUser.__placeholder.1",
|
||||
"title": "",
|
||||
"type": "field",
|
||||
},
|
||||
],
|
||||
"depth": 1,
|
||||
"disabled": false,
|
||||
"key": "__mutation.field.insert_testUser",
|
||||
"title": <SubFieldTitle
|
||||
enabled={true}
|
||||
isSubfield={false}
|
||||
title="insert_testUser"
|
||||
/>,
|
||||
"type": "field",
|
||||
},
|
||||
Object {
|
||||
"checkable": false,
|
||||
"children": Array [
|
||||
Object {
|
||||
"checkable": false,
|
||||
"depth": 2,
|
||||
"disabled": true,
|
||||
"key": "__mutation.field.insert_testUser_one.__placeholder.1",
|
||||
"title": "",
|
||||
"type": "field",
|
||||
},
|
||||
],
|
||||
"depth": 1,
|
||||
"disabled": false,
|
||||
"key": "__mutation.field.insert_testUser_one",
|
||||
"title": <SubFieldTitle
|
||||
enabled={true}
|
||||
isSubfield={false}
|
||||
title="insert_testUser_one"
|
||||
/>,
|
||||
"type": "field",
|
||||
},
|
||||
Object {
|
||||
"checkable": false,
|
||||
"children": Array [
|
||||
Object {
|
||||
"checkable": false,
|
||||
"depth": 2,
|
||||
"disabled": true,
|
||||
"key": "__mutation.field.update_testUser.__placeholder.1",
|
||||
"title": "",
|
||||
"type": "field",
|
||||
},
|
||||
],
|
||||
"depth": 1,
|
||||
"disabled": false,
|
||||
"key": "__mutation.field.update_testUser",
|
||||
"title": <SubFieldTitle
|
||||
enabled={true}
|
||||
isSubfield={false}
|
||||
title="update_testUser"
|
||||
/>,
|
||||
"type": "field",
|
||||
},
|
||||
Object {
|
||||
"checkable": false,
|
||||
"children": Array [
|
||||
Object {
|
||||
"checkable": false,
|
||||
"depth": 2,
|
||||
"disabled": true,
|
||||
"key": "__mutation.field.update_testUser_by_pk.__placeholder.1",
|
||||
"title": "",
|
||||
"type": "field",
|
||||
},
|
||||
],
|
||||
"depth": 1,
|
||||
"disabled": false,
|
||||
"key": "__mutation.field.update_testUser_by_pk",
|
||||
"title": <SubFieldTitle
|
||||
enabled={true}
|
||||
isSubfield={false}
|
||||
title="update_testUser_by_pk"
|
||||
/>,
|
||||
"type": "field",
|
||||
},
|
||||
],
|
||||
"depth": 0,
|
||||
"key": "__mutation",
|
||||
"title": <RootFieldTitle
|
||||
title="Mutation"
|
||||
/>,
|
||||
"type": "field",
|
||||
},
|
||||
Object {
|
||||
"checkable": false,
|
||||
"children": Array [
|
||||
Object {
|
||||
"checkable": false,
|
||||
"children": Array [
|
||||
Object {
|
||||
"checkable": false,
|
||||
"depth": 2,
|
||||
"disabled": true,
|
||||
"key": "__subscription.field.testUser.__placeholder.1",
|
||||
"title": "",
|
||||
"type": "field",
|
||||
},
|
||||
],
|
||||
"depth": 1,
|
||||
"disabled": false,
|
||||
"key": "__subscription.field.testUser",
|
||||
"title": <SubFieldTitle
|
||||
enabled={true}
|
||||
isSubfield={false}
|
||||
title="testUser"
|
||||
/>,
|
||||
"type": "field",
|
||||
},
|
||||
Object {
|
||||
"checkable": false,
|
||||
"children": Array [
|
||||
Object {
|
||||
"checkable": false,
|
||||
"depth": 2,
|
||||
"disabled": true,
|
||||
"key": "__subscription.field.testUser_aggregate.__placeholder.1",
|
||||
"title": "",
|
||||
"type": "field",
|
||||
},
|
||||
],
|
||||
"depth": 1,
|
||||
"disabled": false,
|
||||
"key": "__subscription.field.testUser_aggregate",
|
||||
"title": <SubFieldTitle
|
||||
enabled={true}
|
||||
isSubfield={false}
|
||||
title="testUser_aggregate"
|
||||
/>,
|
||||
"type": "field",
|
||||
},
|
||||
Object {
|
||||
"checkable": false,
|
||||
"children": Array [
|
||||
Object {
|
||||
"checkable": false,
|
||||
"depth": 2,
|
||||
"disabled": true,
|
||||
"key": "__subscription.field.testUser_by_pk.__placeholder.1",
|
||||
"title": "",
|
||||
"type": "field",
|
||||
},
|
||||
],
|
||||
"depth": 1,
|
||||
"disabled": false,
|
||||
"key": "__subscription.field.testUser_by_pk",
|
||||
"title": <SubFieldTitle
|
||||
enabled={true}
|
||||
isSubfield={false}
|
||||
title="testUser_by_pk"
|
||||
/>,
|
||||
"type": "field",
|
||||
},
|
||||
],
|
||||
"depth": 0,
|
||||
"key": "__subscription",
|
||||
"title": <RootFieldTitle
|
||||
title="Subscription"
|
||||
/>,
|
||||
"type": "field",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Utils.ts parseServerRelationship 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"argValue": null,
|
||||
"checkable": false,
|
||||
"depth": 0,
|
||||
"key": "__query",
|
||||
"type": "field",
|
||||
},
|
||||
Object {
|
||||
"argValue": null,
|
||||
"checkable": false,
|
||||
"depth": 1,
|
||||
"key": "__query.field.testUser_aggregate",
|
||||
"type": "field",
|
||||
},
|
||||
Object {
|
||||
"argValue": null,
|
||||
"checkable": false,
|
||||
"depth": 2,
|
||||
"key": "__query.field.testUser_aggregate.field.aggregate",
|
||||
"type": "field",
|
||||
},
|
||||
Object {
|
||||
"argValue": null,
|
||||
"checkable": false,
|
||||
"depth": 3,
|
||||
"key": "__query.field.testUser_aggregate.field.aggregate.field.count",
|
||||
"type": "field",
|
||||
},
|
||||
Object {
|
||||
"argValue": Object {
|
||||
"kind": "column",
|
||||
"type": "String",
|
||||
"value": "firstName",
|
||||
},
|
||||
"checkable": true,
|
||||
"depth": 4,
|
||||
"key": "__query.field.testUser_aggregate.field.aggregate.field.count.arguments.columns",
|
||||
"type": "arg",
|
||||
},
|
||||
]
|
||||
`;
|
@ -0,0 +1,60 @@
|
||||
import { buildClientSchema } from 'graphql';
|
||||
import hasura_schema from '../fixtures/hasura_schema.json';
|
||||
import {
|
||||
getExpandedKeys,
|
||||
getCheckedKeys,
|
||||
buildServerRemoteFieldObject,
|
||||
parseServerRelationship,
|
||||
buildTree,
|
||||
} from '../utils';
|
||||
import {
|
||||
relationship_fields,
|
||||
remote_rel_definition,
|
||||
customer_columns,
|
||||
} from '../fixtures/constants';
|
||||
|
||||
describe('Utils.ts', () => {
|
||||
it('buildServerRemoteFieldObject', () => {
|
||||
const remoteRelObj = buildServerRemoteFieldObject(relationship_fields);
|
||||
expect(remoteRelObj).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('parseServerRelationship', () => {
|
||||
const relationshipFields = parseServerRelationship(remote_rel_definition);
|
||||
expect(relationshipFields).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('buildTree', () => {
|
||||
const schema = buildClientSchema(hasura_schema as any);
|
||||
const tree = buildTree(schema, [], () => {}, customer_columns, [
|
||||
'query',
|
||||
'mutation',
|
||||
'subscription',
|
||||
]);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('getExpandedKeys', () => {
|
||||
const expandedKeys = [
|
||||
'__query',
|
||||
'__query.testUser_aggregate',
|
||||
'__query.testUser_aggregate.arguments.where',
|
||||
'__query.testUser_aggregate.arguments.where.id',
|
||||
'__query.testUser_aggregate.field.aggregate',
|
||||
'__query.testUser_aggregate.field.aggregate.field.count',
|
||||
];
|
||||
expect(getExpandedKeys(relationship_fields)).toEqual(
|
||||
expect.arrayContaining(expandedKeys)
|
||||
);
|
||||
});
|
||||
|
||||
it('gets checked keys', () => {
|
||||
const checkedKeys = [
|
||||
'__query.testUser_aggregate.arguments.where.id._eq',
|
||||
'__query.testUser_aggregate.field.aggregate.field.count.arguments.distinct',
|
||||
];
|
||||
expect(getCheckedKeys(relationship_fields)).toEqual(
|
||||
expect.arrayContaining(checkedKeys)
|
||||
);
|
||||
});
|
||||
});
|
@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import { ArgValueForm } from './ArgValueForm';
|
||||
import { RelationshipFields, ArgValue, HasuraColumn } from '../types';
|
||||
|
||||
type ArgFieldTitleProps = {
|
||||
title: string;
|
||||
argKey: string;
|
||||
relationshipFields: RelationshipFields[];
|
||||
setRelationshipFields: React.Dispatch<
|
||||
React.SetStateAction<RelationshipFields[]>
|
||||
>;
|
||||
showForm: boolean;
|
||||
argValue: ArgValue;
|
||||
columns: HasuraColumn;
|
||||
};
|
||||
|
||||
const titleStyles =
|
||||
'flex items-center cursor-pointer text-purple-600 whitespace-nowrap hover:text-purple-900';
|
||||
|
||||
export const ArgFieldTitle = ({
|
||||
title,
|
||||
argKey,
|
||||
relationshipFields,
|
||||
setRelationshipFields,
|
||||
showForm,
|
||||
argValue,
|
||||
columns,
|
||||
}: ArgFieldTitleProps) => {
|
||||
return showForm ? (
|
||||
<>
|
||||
<div className={titleStyles}>{title}</div>
|
||||
<ArgValueForm
|
||||
argKey={argKey}
|
||||
relationshipFields={relationshipFields}
|
||||
setRelationshipFields={setRelationshipFields}
|
||||
argValue={argValue}
|
||||
columns={columns}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div className={titleStyles}>{title}</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,165 @@
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { useDebouncedEffect } from '@/hooks/useDebounceEffect';
|
||||
import {
|
||||
ArgValue,
|
||||
ArgValueKind,
|
||||
HasuraColumn,
|
||||
RelationshipFields,
|
||||
} from '../types';
|
||||
import { defaultArgValue } from '../utils';
|
||||
|
||||
export interface ArgValueFormProps {
|
||||
argKey: string;
|
||||
relationshipFields: RelationshipFields[];
|
||||
setRelationshipFields: React.Dispatch<
|
||||
React.SetStateAction<RelationshipFields[]>
|
||||
>;
|
||||
argValue: ArgValue;
|
||||
columns: HasuraColumn;
|
||||
}
|
||||
|
||||
const fieldStyle =
|
||||
'block w-full h-input shadow-sm rounded border border-gray-300 hover:border-gray-400 focus:outline-0 focus:ring-2 focus:ring-yellow-200 focus:border-yellow-400';
|
||||
|
||||
const argValueTypeOptions = [
|
||||
{ key: 'column', content: 'Source Column' },
|
||||
{ key: 'static', content: 'Static Value' },
|
||||
];
|
||||
|
||||
export const ArgValueForm = ({
|
||||
argKey,
|
||||
relationshipFields,
|
||||
setRelationshipFields,
|
||||
argValue,
|
||||
columns,
|
||||
}: ArgValueFormProps) => {
|
||||
const allColumns = useMemo(
|
||||
() => [...(columns.columns ?? []), ...(columns.computedFields ?? [])],
|
||||
[columns]
|
||||
);
|
||||
const [localArgValue, setLocalArgValue] = useState(argValue);
|
||||
|
||||
useEffect(() => {
|
||||
setLocalArgValue(argValue);
|
||||
}, [argValue]);
|
||||
|
||||
useDebouncedEffect(
|
||||
() => {
|
||||
setRelationshipFields(
|
||||
relationshipFields.map(f => {
|
||||
if (f.key === argKey) {
|
||||
return {
|
||||
...f,
|
||||
argValue: {
|
||||
...(f.argValue ?? defaultArgValue),
|
||||
value: localArgValue.value,
|
||||
},
|
||||
};
|
||||
}
|
||||
return f;
|
||||
})
|
||||
);
|
||||
},
|
||||
400,
|
||||
[localArgValue.value]
|
||||
);
|
||||
|
||||
const changeInputType = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
setRelationshipFields(
|
||||
relationshipFields.map(f => {
|
||||
if (f.key === argKey) {
|
||||
return {
|
||||
...f,
|
||||
argValue: {
|
||||
...(f.argValue ?? defaultArgValue),
|
||||
value: '',
|
||||
kind: e.target.value as ArgValueKind,
|
||||
},
|
||||
};
|
||||
}
|
||||
return f;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const changeInputColumnValue = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
setRelationshipFields(
|
||||
relationshipFields.map(f => {
|
||||
if (f.key === argKey) {
|
||||
return {
|
||||
...f,
|
||||
argValue: {
|
||||
...(f.argValue ?? defaultArgValue),
|
||||
value: e.target.value,
|
||||
},
|
||||
};
|
||||
}
|
||||
return f;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const onValueChangeHandler = (value: string) => {
|
||||
setLocalArgValue({ ...localArgValue, value });
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={e => e.stopPropagation()}
|
||||
className="rounded bg-white shadow pt-xs pb-sm px-sm mb-sm border-l-2 border-yellow-400 w-full"
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div>
|
||||
<p className="mb-xs text-muted font-semibold">Fill From</p>
|
||||
<select
|
||||
className={fieldStyle}
|
||||
value={localArgValue.kind}
|
||||
onChange={changeInputType}
|
||||
>
|
||||
<option disabled>Select an arugment...</option>
|
||||
{argValueTypeOptions.map(option => (
|
||||
<option key={option.key} value={option.key}>
|
||||
{option.content}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
{localArgValue.kind === 'column' ? (
|
||||
<>
|
||||
<p className="mb-xs text-muted font-semibold">
|
||||
<i className="fa fa-columns text-sm mr-xs" /> From Column
|
||||
</p>
|
||||
<select
|
||||
className={fieldStyle}
|
||||
value={localArgValue.value}
|
||||
onChange={changeInputColumnValue}
|
||||
>
|
||||
<option value="" disabled>
|
||||
Select Column...
|
||||
</option>
|
||||
{allColumns.map(option => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p className="mb-xs text-muted font-semibold">Static Value</p>
|
||||
<input
|
||||
type="text"
|
||||
name="argValue"
|
||||
id="argValue"
|
||||
className={fieldStyle}
|
||||
value={localArgValue.value}
|
||||
onChange={e => onValueChangeHandler(e.target.value)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
|
||||
type FieldLabelProps = {
|
||||
title: string;
|
||||
};
|
||||
|
||||
export const FieldLabel = ({ title }: FieldLabelProps) => {
|
||||
return (
|
||||
<div className="text-xs pt-2 uppercase text-gray-400 tracking-wide font-semibold">
|
||||
{title}
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
import { FaProjectDiagram } from 'react-icons/fa';
|
||||
|
||||
type RootFieldTitleProps = {
|
||||
title: string;
|
||||
};
|
||||
|
||||
export const RootFieldTitle = ({ title }: RootFieldTitleProps) => {
|
||||
return (
|
||||
<div className="flex font-semibold items-center cursor-pointer text-gray-900 w-max whitespace-nowrap hover:text-gray-900">
|
||||
<FaProjectDiagram className="w-4 mr-xs h-5 w-5" /> {title}
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,34 @@
|
||||
import { ToolTip } from '@/new-components/Tooltip';
|
||||
import React from 'react';
|
||||
|
||||
type SubFieldTitleProps = {
|
||||
title: string;
|
||||
enabled?: boolean;
|
||||
isSubfield?: boolean;
|
||||
};
|
||||
|
||||
export const SubFieldTitle = ({
|
||||
title,
|
||||
enabled,
|
||||
isSubfield,
|
||||
}: SubFieldTitleProps) => {
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center cursor-pointer w-max whitespace-nowrap">
|
||||
{!enabled ? (
|
||||
<>
|
||||
<ToolTip
|
||||
className="mr-sm text-gray-400"
|
||||
message="Only fields with arguments or subfields can be toggled"
|
||||
/>
|
||||
<span className="text-gray-400">{title}</span>
|
||||
</>
|
||||
) : isSubfield ? (
|
||||
<span className="text-blue-600 hover:text-blue-900">{title}</span>
|
||||
) : (
|
||||
<span className="text-gray-900">{title}</span>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,99 @@
|
||||
import { RelationshipFields, RemoteRelationship } from '../types';
|
||||
|
||||
export const customer_columns = {
|
||||
columns: ['id', 'firstName', 'lastName', 'age', 'countryCode', 'country'],
|
||||
computedFields: ['field1', 'field2'],
|
||||
};
|
||||
|
||||
export const remote_rel_definition: RemoteRelationship = {
|
||||
definition: {
|
||||
remote_field: {
|
||||
testUser_aggregate: {
|
||||
field: {
|
||||
aggregate: {
|
||||
field: {
|
||||
count: {
|
||||
arguments: {
|
||||
columns: '$firstName',
|
||||
},
|
||||
},
|
||||
},
|
||||
arguments: {},
|
||||
},
|
||||
},
|
||||
arguments: {},
|
||||
},
|
||||
},
|
||||
hasura_fields: ['name'],
|
||||
remote_schema: 'hasura_cloud',
|
||||
},
|
||||
name: 'some_relationship',
|
||||
};
|
||||
|
||||
export const relationship_fields: RelationshipFields[] = [
|
||||
{
|
||||
key: '__query',
|
||||
depth: 0,
|
||||
checkable: false,
|
||||
argValue: null,
|
||||
type: 'field',
|
||||
},
|
||||
{
|
||||
key: '__query.testUser_aggregate',
|
||||
depth: 1,
|
||||
checkable: false,
|
||||
argValue: null,
|
||||
type: 'field',
|
||||
},
|
||||
{
|
||||
key: '__query.testUser_aggregate.arguments.where',
|
||||
depth: 1,
|
||||
checkable: false,
|
||||
argValue: null,
|
||||
type: 'arg',
|
||||
},
|
||||
{
|
||||
key: '__query.testUser_aggregate.arguments.where.id',
|
||||
depth: 2,
|
||||
checkable: false,
|
||||
argValue: null,
|
||||
type: 'arg',
|
||||
},
|
||||
{
|
||||
key: '__query.testUser_aggregate.arguments.where.id._eq',
|
||||
depth: 3,
|
||||
checkable: true,
|
||||
argValue: {
|
||||
kind: 'column',
|
||||
value: 'id',
|
||||
type: 'String',
|
||||
},
|
||||
type: 'arg',
|
||||
},
|
||||
{
|
||||
key: '__query.testUser_aggregate.field.aggregate',
|
||||
depth: 2,
|
||||
checkable: false,
|
||||
argValue: null,
|
||||
type: 'field',
|
||||
},
|
||||
{
|
||||
key: '__query.testUser_aggregate.field.aggregate.field.count',
|
||||
depth: 3,
|
||||
checkable: false,
|
||||
argValue: null,
|
||||
type: 'field',
|
||||
},
|
||||
{
|
||||
key:
|
||||
'__query.testUser_aggregate.field.aggregate.field.count.arguments.distinct',
|
||||
depth: 3,
|
||||
checkable: true,
|
||||
argValue: {
|
||||
kind: 'column',
|
||||
value: 'id',
|
||||
type: 'String',
|
||||
},
|
||||
type: 'arg',
|
||||
},
|
||||
];
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,74 @@
|
||||
import { EventDataNode } from 'antd/lib/tree';
|
||||
|
||||
export type HasuraColumn = {
|
||||
columns: string[];
|
||||
computedFields: string[];
|
||||
};
|
||||
|
||||
export type AllowedRootFields = ('query' | 'mutation' | 'subscription')[];
|
||||
|
||||
export type ArgValueKind = 'column' | 'static';
|
||||
|
||||
export type ArgValue = {
|
||||
kind: ArgValueKind;
|
||||
value: string;
|
||||
type: string;
|
||||
};
|
||||
|
||||
export type RelationshipFields = {
|
||||
key: string;
|
||||
argValue: ArgValue | null;
|
||||
checkable: boolean;
|
||||
depth: number;
|
||||
type: 'field' | 'arg';
|
||||
};
|
||||
|
||||
export type TreeNode = {
|
||||
title: JSX.Element | string;
|
||||
key: string;
|
||||
checkable: boolean;
|
||||
depth: number;
|
||||
type: 'field' | 'arg';
|
||||
disabled?: boolean;
|
||||
argValue?: ArgValue | null;
|
||||
children?: TreeNode[];
|
||||
};
|
||||
|
||||
export type RemoteField = {
|
||||
[FieldName: string]: {
|
||||
arguments: InputArgumentsType | never;
|
||||
field?: RemoteField;
|
||||
};
|
||||
};
|
||||
|
||||
export type InputArgumentValueType =
|
||||
| string
|
||||
| boolean
|
||||
| number
|
||||
| InputArgumentsType;
|
||||
|
||||
export type InputArgumentsType = {
|
||||
[key: string]: InputArgumentValueType;
|
||||
};
|
||||
|
||||
// we should extend RemoteRelationship type from `metadata/types.ts` once we have
|
||||
// the correct types in place, for both old and new format
|
||||
export type RemoteRelationship = {
|
||||
name: string;
|
||||
definition: {
|
||||
hasura_fields: string[];
|
||||
remote_schema: string;
|
||||
remote_field: RemoteField;
|
||||
};
|
||||
};
|
||||
|
||||
export interface AntdTreeNode extends EventDataNode {
|
||||
title: JSX.Element | string;
|
||||
key: string;
|
||||
checkable: boolean;
|
||||
depth: number;
|
||||
type: 'field' | 'arg';
|
||||
disabled?: boolean;
|
||||
argValue?: ArgValue | null;
|
||||
children?: TreeNode[];
|
||||
}
|
@ -0,0 +1,560 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
isInputObjectType,
|
||||
isInterfaceType,
|
||||
isObjectType,
|
||||
GraphQLSchema,
|
||||
GraphQLField,
|
||||
GraphQLType,
|
||||
GraphQLArgument,
|
||||
GraphQLInputField,
|
||||
isWrappingType,
|
||||
isListType,
|
||||
isNonNullType,
|
||||
} from 'graphql';
|
||||
import { isEmpty, isFloat, isNumber } from '@/components/Common/utils/jsUtils';
|
||||
import {
|
||||
AllowedRootFields,
|
||||
ArgValue,
|
||||
HasuraColumn,
|
||||
RelationshipFields,
|
||||
TreeNode,
|
||||
RemoteRelationship,
|
||||
RemoteField,
|
||||
InputArgumentsType,
|
||||
InputArgumentValueType,
|
||||
AntdTreeNode,
|
||||
} from './types';
|
||||
import { SubFieldTitle } from './components/SubFieldTitle';
|
||||
import { RootFieldTitle } from './components/RootFieldTitle';
|
||||
import { FieldLabel } from './components/FieldLabel';
|
||||
import { ArgFieldTitle } from './components/ArgFieldTitle';
|
||||
|
||||
export const getFieldData = (nodeData: AntdTreeNode): RelationshipFields => ({
|
||||
key: nodeData.key,
|
||||
depth: nodeData.depth,
|
||||
checkable: nodeData.checkable,
|
||||
argValue: nodeData.argValue ?? null,
|
||||
type: nodeData.type,
|
||||
});
|
||||
|
||||
export const defaultArgValue: ArgValue = {
|
||||
kind: 'column',
|
||||
value: '',
|
||||
type: 'String',
|
||||
};
|
||||
|
||||
export const findRemoteField = (
|
||||
fields: RelationshipFields[],
|
||||
field: AntdTreeNode
|
||||
) => {
|
||||
return fields.find(f => f.key === field.key);
|
||||
};
|
||||
|
||||
const isElementActive = (
|
||||
relationshipFields: RelationshipFields[],
|
||||
fieldKey: string
|
||||
) => {
|
||||
return relationshipFields.some(f => f.key === fieldKey);
|
||||
};
|
||||
|
||||
export const getUnderlyingType = (_type: Record<string, any>) => {
|
||||
let type = Object.assign(Object.create(_type), _type);
|
||||
const wraps = [];
|
||||
while (isWrappingType(type)) {
|
||||
if (isListType(type)) wraps.push('l');
|
||||
if (isNonNullType(type)) wraps.push('n');
|
||||
type = type.ofType;
|
||||
}
|
||||
return {
|
||||
wraps,
|
||||
type,
|
||||
};
|
||||
};
|
||||
|
||||
/* returns checked value if arg is checked
|
||||
* returns null if arg isn't involved in the relationship
|
||||
*/
|
||||
export const getCheckedArgValue = (
|
||||
relationshipFields: RelationshipFields[],
|
||||
key: string
|
||||
): ArgValue | null => {
|
||||
const field = relationshipFields.find(r => r.key === key);
|
||||
if (field) {
|
||||
return field.argValue;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const getPlaceholderChild = (parentKey: string, depth: number): TreeNode => ({
|
||||
title: '',
|
||||
key: `${parentKey}.__placeholder.${depth}`,
|
||||
depth: depth + 1,
|
||||
checkable: false,
|
||||
type: 'field',
|
||||
disabled: true,
|
||||
});
|
||||
|
||||
const buildArgElement = ({
|
||||
arg,
|
||||
parentKey,
|
||||
relationshipFields,
|
||||
setRelationshipFields,
|
||||
columns,
|
||||
depth,
|
||||
}: {
|
||||
arg: GraphQLArgument | GraphQLInputField;
|
||||
parentKey: string;
|
||||
relationshipFields: RelationshipFields[];
|
||||
setRelationshipFields: React.Dispatch<
|
||||
React.SetStateAction<RelationshipFields[]>
|
||||
>;
|
||||
columns: HasuraColumn;
|
||||
depth: number;
|
||||
}): TreeNode => {
|
||||
const { type: argType }: { type: GraphQLType } = getUnderlyingType(arg.type);
|
||||
let children: TreeNode[] = [];
|
||||
let checkable = true;
|
||||
const argKey = `${parentKey}.${arg.name}`;
|
||||
const isActive = isElementActive(relationshipFields, argKey);
|
||||
const argValue = getCheckedArgValue(relationshipFields, argKey);
|
||||
|
||||
if (isInputObjectType(argType) || isInterfaceType(argType)) {
|
||||
const argFields = argType.getFields();
|
||||
if (!isEmpty(argFields)) {
|
||||
checkable = false;
|
||||
children = [getPlaceholderChild(argKey, depth + 1)];
|
||||
if (isActive) {
|
||||
children = [
|
||||
...Object.values(argFields).map(argField =>
|
||||
buildArgElement({
|
||||
arg: argField,
|
||||
parentKey: argKey,
|
||||
relationshipFields,
|
||||
setRelationshipFields,
|
||||
columns,
|
||||
depth: depth + 1,
|
||||
})
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
title: (
|
||||
<ArgFieldTitle
|
||||
title={arg.name}
|
||||
argKey={argKey}
|
||||
relationshipFields={relationshipFields}
|
||||
setRelationshipFields={setRelationshipFields}
|
||||
columns={columns}
|
||||
showForm={isActive && checkable}
|
||||
argValue={argValue || defaultArgValue}
|
||||
/>
|
||||
),
|
||||
key: argKey,
|
||||
depth,
|
||||
type: 'arg',
|
||||
checkable,
|
||||
argValue: checkable ? argValue || defaultArgValue : null,
|
||||
children,
|
||||
};
|
||||
};
|
||||
|
||||
const buildFieldElement = ({
|
||||
field,
|
||||
parentKey,
|
||||
relationshipFields,
|
||||
setRelationshipFields,
|
||||
columns,
|
||||
depth,
|
||||
isSubfield,
|
||||
}: {
|
||||
field: GraphQLField<any, any, Record<string, any>>;
|
||||
parentKey: string;
|
||||
relationshipFields: RelationshipFields[];
|
||||
setRelationshipFields: React.Dispatch<
|
||||
React.SetStateAction<RelationshipFields[]>
|
||||
>;
|
||||
columns: HasuraColumn;
|
||||
depth: number;
|
||||
isSubfield: boolean;
|
||||
}): TreeNode => {
|
||||
const { type: fieldType }: { type: GraphQLType } = getUnderlyingType(
|
||||
field.type
|
||||
);
|
||||
const fieldKey = `${parentKey}.${field.name}`;
|
||||
const enabled =
|
||||
(field.args && !!field.args.length) || isObjectType(fieldType);
|
||||
|
||||
let children: TreeNode[] = [];
|
||||
if (enabled) {
|
||||
children = [getPlaceholderChild(fieldKey, depth + 1)];
|
||||
}
|
||||
|
||||
const isActive = isElementActive(relationshipFields, fieldKey);
|
||||
if (isActive) {
|
||||
children = [];
|
||||
if (field.args && !!field.args.length) {
|
||||
children = [
|
||||
{
|
||||
title: <FieldLabel title="Arguments" />,
|
||||
key: `${fieldKey}.__placeholder.args.${depth}`,
|
||||
checkable: false,
|
||||
type: 'field',
|
||||
depth,
|
||||
},
|
||||
...field.args.map(arg =>
|
||||
buildArgElement({
|
||||
arg,
|
||||
parentKey: `${fieldKey}.arguments`,
|
||||
relationshipFields,
|
||||
setRelationshipFields,
|
||||
columns,
|
||||
depth: depth + 1,
|
||||
})
|
||||
),
|
||||
];
|
||||
}
|
||||
if (isObjectType(fieldType) || isInterfaceType(fieldType)) {
|
||||
const subFields = fieldType.getFields();
|
||||
if (!isEmpty(subFields)) {
|
||||
children = [
|
||||
...children,
|
||||
{
|
||||
title: <FieldLabel title="Sub-Fields" />,
|
||||
key: `${fieldKey}.__placeholder.sub_fields.${depth}`,
|
||||
checkable: false,
|
||||
type: 'field',
|
||||
depth,
|
||||
},
|
||||
...Object.values(subFields).map(subField =>
|
||||
buildFieldElement({
|
||||
field: subField,
|
||||
parentKey: `${fieldKey}.field`,
|
||||
relationshipFields,
|
||||
setRelationshipFields,
|
||||
columns,
|
||||
depth: depth + 1,
|
||||
isSubfield: true,
|
||||
})
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
title: (
|
||||
<SubFieldTitle
|
||||
title={field.name}
|
||||
enabled={enabled}
|
||||
isSubfield={isSubfield}
|
||||
/>
|
||||
),
|
||||
key: fieldKey,
|
||||
depth: depth + 1,
|
||||
checkable: false,
|
||||
type: 'field',
|
||||
disabled: !enabled,
|
||||
children,
|
||||
};
|
||||
};
|
||||
|
||||
export const buildTree = (
|
||||
schema: GraphQLSchema,
|
||||
relationshipFields: RelationshipFields[],
|
||||
setRelationshipFields: React.Dispatch<
|
||||
React.SetStateAction<RelationshipFields[]>
|
||||
>,
|
||||
columns: HasuraColumn,
|
||||
rootFields: AllowedRootFields
|
||||
): TreeNode[] => {
|
||||
const treeData: TreeNode[] = [];
|
||||
if (rootFields.includes('query')) {
|
||||
const queryType = schema.getQueryType();
|
||||
const fields = queryType?.getFields();
|
||||
const fieldKey = '__query';
|
||||
if (fields) {
|
||||
treeData.push({
|
||||
title: <RootFieldTitle title="Query" />,
|
||||
key: fieldKey,
|
||||
checkable: false,
|
||||
depth: 0,
|
||||
type: 'field',
|
||||
children: Object.values(fields).map(field =>
|
||||
buildFieldElement({
|
||||
field,
|
||||
parentKey: `${fieldKey}.field`,
|
||||
relationshipFields,
|
||||
setRelationshipFields,
|
||||
columns,
|
||||
depth: 0,
|
||||
isSubfield: false,
|
||||
})
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
if (rootFields.includes('mutation')) {
|
||||
const mutationType = schema.getMutationType();
|
||||
const fields = mutationType?.getFields();
|
||||
const fieldKey = '__mutation';
|
||||
if (fields) {
|
||||
treeData.push({
|
||||
title: <RootFieldTitle title="Mutation" />,
|
||||
key: fieldKey,
|
||||
checkable: false,
|
||||
depth: 0,
|
||||
type: 'field',
|
||||
children: Object.values(fields).map(field =>
|
||||
buildFieldElement({
|
||||
field,
|
||||
parentKey: `${fieldKey}.field`,
|
||||
relationshipFields,
|
||||
setRelationshipFields,
|
||||
columns,
|
||||
depth: 0,
|
||||
isSubfield: false,
|
||||
})
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
if (rootFields.includes('subscription')) {
|
||||
const subscriptionType = schema.getSubscriptionType();
|
||||
const fields = subscriptionType?.getFields();
|
||||
const fieldKey = '__subscription';
|
||||
if (fields) {
|
||||
treeData.push({
|
||||
title: <RootFieldTitle title="Subscription" />,
|
||||
key: fieldKey,
|
||||
checkable: false,
|
||||
depth: 0,
|
||||
type: 'field',
|
||||
children: Object.values(fields).map(field =>
|
||||
buildFieldElement({
|
||||
field,
|
||||
parentKey: `${fieldKey}.field`,
|
||||
relationshipFields,
|
||||
setRelationshipFields,
|
||||
columns,
|
||||
depth: 0,
|
||||
isSubfield: false,
|
||||
})
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
return treeData;
|
||||
};
|
||||
|
||||
const getRemoteFieldObject = (
|
||||
ukSplit: string[][],
|
||||
depth: number,
|
||||
maxDepth: number
|
||||
): RemoteField | InputArgumentsType | undefined => {
|
||||
if (depth > maxDepth) return;
|
||||
const obj: RemoteField | InputArgumentsType = {};
|
||||
const depthRemoteFields: string[] = ukSplit.map(uk => uk[depth] ?? '');
|
||||
|
||||
// get unique depth remote fields at current depth
|
||||
const uniqueDepthRemoteFields = Array.from(new Set(depthRemoteFields));
|
||||
uniqueDepthRemoteFields.forEach((uniqueField, i) => {
|
||||
if (uniqueField.length > 0) {
|
||||
const newUkSplit: string[][] = [];
|
||||
depthRemoteFields.forEach((depthField, index) => {
|
||||
if (depthField === uniqueField) {
|
||||
newUkSplit.push(ukSplit[index]);
|
||||
}
|
||||
});
|
||||
|
||||
// if leaf, push arg value
|
||||
if (ukSplit[i][depth + 1] === '__argVal') {
|
||||
obj[uniqueField] = ukSplit[i][depth + 2];
|
||||
} else {
|
||||
obj[uniqueField] = {
|
||||
...getRemoteFieldObject(newUkSplit, depth + 1, maxDepth),
|
||||
};
|
||||
if (ukSplit[i][depth - 1] === 'field') {
|
||||
obj[uniqueField] = {
|
||||
...(obj[uniqueField] as RemoteField),
|
||||
arguments: {
|
||||
...(obj[uniqueField] as RemoteField).arguments,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return Object.keys(obj).length ? obj : undefined;
|
||||
};
|
||||
|
||||
const getUniqueRelFields = (relationshipFields: RelationshipFields[]) => {
|
||||
return relationshipFields?.filter(
|
||||
f =>
|
||||
!relationshipFields.some(
|
||||
refF => refF.key !== f.key && refF.key.includes(f.key)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const getKeysWithArgValues = (relationshipFields: RelationshipFields[]) =>
|
||||
relationshipFields?.map(field => {
|
||||
if (field.type === 'arg') {
|
||||
if (field.argValue && field.argValue?.kind === 'static')
|
||||
return `${field.key}.__argVal.${field.argValue?.value}`;
|
||||
else if (field.argValue && field.argValue?.kind === 'column')
|
||||
return `${field.key}.__argVal.$${field.argValue?.value}`;
|
||||
return `${field.key}.__argVal.$`;
|
||||
}
|
||||
return field.key;
|
||||
});
|
||||
|
||||
export const buildServerRemoteFieldObject = (
|
||||
relationshipFields: RelationshipFields[]
|
||||
) => {
|
||||
const uniqueRelFields = getUniqueRelFields(relationshipFields);
|
||||
const uniqueKeys = getKeysWithArgValues(uniqueRelFields);
|
||||
const ukSplit: string[][] = [];
|
||||
uniqueKeys.forEach(uk => ukSplit.push(uk.split('.')));
|
||||
let maxDepth = 0;
|
||||
ukSplit.forEach(ar => {
|
||||
if (ar.length > maxDepth) maxDepth = ar.length;
|
||||
});
|
||||
|
||||
const relationshipObject = getRemoteFieldObject(ukSplit, 2, maxDepth);
|
||||
return (relationshipObject as RemoteField) || {};
|
||||
};
|
||||
|
||||
export const getExpandedKeys = (relationshipFields: RelationshipFields[]) =>
|
||||
relationshipFields.filter(rf => !rf.argValue).map(rf => rf.key);
|
||||
|
||||
export const getCheckedKeys = (relationshipFields: RelationshipFields[]) =>
|
||||
relationshipFields.filter(rf => rf.argValue).map(rf => rf.key);
|
||||
|
||||
export const parseArgValue = (
|
||||
argValue: InputArgumentValueType
|
||||
): ArgValue | null => {
|
||||
if (typeof argValue === 'object' && argValue !== null) {
|
||||
return null;
|
||||
}
|
||||
if (typeof argValue === 'string') {
|
||||
const isStatic = !argValue.startsWith('$');
|
||||
return {
|
||||
value: isStatic ? argValue.toString() : argValue.substr(1),
|
||||
kind: isStatic ? 'static' : 'column',
|
||||
type: 'String',
|
||||
};
|
||||
}
|
||||
if (typeof argValue === 'boolean') {
|
||||
return {
|
||||
kind: 'static',
|
||||
value: argValue.toString(),
|
||||
type: 'Boolean',
|
||||
};
|
||||
}
|
||||
return {
|
||||
kind: 'static',
|
||||
value: argValue.toString(),
|
||||
type:
|
||||
(isNumber(argValue) && (isFloat(argValue) ? 'Float' : 'Int')) || 'String',
|
||||
};
|
||||
};
|
||||
|
||||
const serialiseArguments = (
|
||||
args: InputArgumentValueType,
|
||||
key: string,
|
||||
depth: number,
|
||||
callback: (f: RelationshipFields) => void
|
||||
): void => {
|
||||
if (typeof args === 'object') {
|
||||
Object.keys(args).forEach(argName => {
|
||||
const argValue = args[argName];
|
||||
const argValueMetadata = parseArgValue(argValue);
|
||||
if (argValueMetadata) {
|
||||
callback({
|
||||
key: `${key}.${argName}`,
|
||||
depth,
|
||||
checkable: true,
|
||||
argValue: argValueMetadata,
|
||||
type: 'arg',
|
||||
});
|
||||
} else {
|
||||
callback({
|
||||
key: `${key}.${argName}`,
|
||||
depth,
|
||||
checkable: false,
|
||||
argValue: null,
|
||||
type: 'arg',
|
||||
});
|
||||
serialiseArguments(argValue, `${key}.${argName}`, depth + 1, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const serialiseRemoteField = (
|
||||
field: {
|
||||
arguments: InputArgumentsType | never;
|
||||
field?: RemoteField | undefined;
|
||||
},
|
||||
key: string,
|
||||
depth: number,
|
||||
callback: (f: RelationshipFields) => void
|
||||
): void => {
|
||||
callback({
|
||||
key,
|
||||
depth,
|
||||
checkable: false,
|
||||
argValue: null,
|
||||
type: 'field',
|
||||
});
|
||||
if (field.field) {
|
||||
const subFieldName = field.field ? Object.keys(field.field)[0] : '';
|
||||
const subField = field.field?.[subFieldName];
|
||||
serialiseRemoteField(
|
||||
subField,
|
||||
`${key}.field.${subFieldName}`,
|
||||
depth + 1,
|
||||
callback
|
||||
);
|
||||
}
|
||||
if (field.arguments) {
|
||||
serialiseArguments(
|
||||
field.arguments,
|
||||
`${key}.arguments`,
|
||||
depth + 1,
|
||||
callback
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: this only parses the remote relationship in old format, and the type `RemoteRelationship` is old format
|
||||
// we should extend this for both old & new format once the server work is done, and remove this comment
|
||||
export const parseServerRelationship = (
|
||||
serverRelationship: RemoteRelationship
|
||||
): RelationshipFields[] => {
|
||||
const remoteFields = serverRelationship?.definition?.remote_field;
|
||||
if (!remoteFields || isEmpty(remoteFields)) {
|
||||
return [];
|
||||
}
|
||||
// only query root is supported by server relationships, so we only expect fields under query root
|
||||
// this could be extended to use mutation and subscription if server support exists in future
|
||||
const key = '__query';
|
||||
const depth = 0;
|
||||
const relationshipFields: RelationshipFields[] = [
|
||||
{ key, depth, checkable: false, argValue: null, type: 'field' },
|
||||
];
|
||||
|
||||
Object.keys(remoteFields).forEach(rf => {
|
||||
serialiseRemoteField(
|
||||
remoteFields[rf],
|
||||
`${key}.field.${rf}`,
|
||||
depth + 1,
|
||||
(field: RelationshipFields) => relationshipFields.push(field)
|
||||
);
|
||||
});
|
||||
return relationshipFields;
|
||||
};
|
Loading…
Reference in New Issue
Block a user