From c64f1108f0ecb396f85239b308d1d1f993fda683 Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Tue, 30 Jan 2024 16:43:54 -0800 Subject: [PATCH] Initial frontend for gRPC UI --- package-lock.json | 302 ++++++++++++++++-- package.json | 2 + src-tauri/src/main.rs | 12 +- src-tauri/src/models.rs | 84 ++++- src-web/components/GraphQLEditor.tsx | 4 +- src-web/components/GrpcConnectionLayout.tsx | 81 +++++ src-web/components/GrpcEditor.tsx | 40 +++ src-web/components/HttpRequestLayout.tsx | 21 ++ src-web/components/RequestPane.tsx | 33 +- src-web/components/UrlBar.tsx | 72 ++--- src-web/components/Workspace.tsx | 11 +- src-web/components/core/Editor/extensions.ts | 4 +- .../SplitLayout.tsx} | 53 +-- src-web/hooks/useCreateCookieJar.ts | 14 +- src-web/hooks/useGrpc.ts | 35 ++ src-web/main.tsx | 14 - 16 files changed, 650 insertions(+), 132 deletions(-) create mode 100644 src-web/components/GrpcConnectionLayout.tsx create mode 100644 src-web/components/GrpcEditor.tsx create mode 100644 src-web/components/HttpRequestLayout.tsx rename src-web/components/{RequestResponse.tsx => core/SplitLayout.tsx} (74%) create mode 100644 src-web/hooks/useGrpc.ts diff --git a/package-lock.json b/package-lock.json index 48ded9bf..42b4fa10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,8 @@ "classnames": "^2.3.2", "cm6-graphql": "^0.0.9", "codemirror": "^6.0.1", + "codemirror-json-schema": "^0.6.1", + "codemirror-json5": "^1.0.3", "focus-trap-react": "^10.1.1", "format-graphql": "^1.4.0", "framer-motion": "^9.0.4", @@ -469,6 +471,30 @@ "node": ">=6.9.0" } }, + "node_modules/@changesets/changelog-github": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/@changesets/changelog-github/-/changelog-github-0.4.8.tgz", + "integrity": "sha512-jR1DHibkMAb5v/8ym77E4AMNWZKB5NPzw5a5Wtqm1JepAuIF+hrKp2u04NKM14oBZhHglkCfrla9uq8ORnK/dw==", + "dependencies": { + "@changesets/get-github-info": "^0.5.2", + "@changesets/types": "^5.2.1", + "dotenv": "^8.1.0" + } + }, + "node_modules/@changesets/get-github-info": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@changesets/get-github-info/-/get-github-info-0.5.2.tgz", + "integrity": "sha512-JppheLu7S114aEs157fOZDjFqUDpm7eHdq5E8SSR0gUBTEK0cNSHsrSR5a66xs0z3RWuo46QvA3vawp8BxDHvg==", + "dependencies": { + "dataloader": "^1.4.0", + "node-fetch": "^2.5.0" + } + }, + "node_modules/@changesets/types": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-5.2.1.tgz", + "integrity": "sha512-myLfHbVOqaq9UtUKqR/nZA/OY7xFjQMdfgfqeZIBK4d0hA6pgxArvdv8M+6NUzzBsjWLOtvApv8YHr4qM+Kpfg==" + }, "node_modules/@codemirror/autocomplete": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.2.0.tgz", @@ -511,16 +537,6 @@ "@lezer/javascript": "^1.0.0" } }, - "node_modules/@codemirror/lang-javascript/node_modules/@codemirror/view": { - "version": "6.21.4", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.21.4.tgz", - "integrity": "sha512-WKVZ7nvN0lwWPfAf05WxWqTpwjC8YN3q5goj3CsSig7//DD81LULgOx3nBegqpqP0iygBqRmW8z0KSc2QTAdAg==", - "dependencies": { - "@codemirror/state": "^6.1.4", - "style-mod": "^4.1.0", - "w3c-keyname": "^2.2.4" - } - }, "node_modules/@codemirror/lang-json": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz", @@ -556,9 +572,9 @@ } }, "node_modules/@codemirror/lint": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.2.1.tgz", - "integrity": "sha512-y1muai5U/uUPAGRyHMx9mHuHLypPcHWxzlZGknp/U5Mdb5Ol8Q5ZLp67UqyTbNFJJ3unVxZ8iX3g1fMN79S1JQ==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.4.2.tgz", + "integrity": "sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==", "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", @@ -576,17 +592,17 @@ } }, "node_modules/@codemirror/state": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.2.0.tgz", - "integrity": "sha512-69QXtcrsc3RYtOtd+GsvczJ319udtBf1PTrr2KbLWM/e2CXUPnh0Nz9AUo8WfhSQ7GeL8dPVNUmhQVgpmuaNGA==" + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.0.tgz", + "integrity": "sha512-hm8XshYj5Fo30Bb922QX9hXB/bxOAVH+qaqHBzw5TKa72vOeslyGwd4X8M0c1dJ9JqxlaMceOQ8RsL9tC7gU0A==" }, "node_modules/@codemirror/view": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.2.1.tgz", - "integrity": "sha512-r1svbtAj2Lp/86F3yy1TfDAOAtJRGLINLSEqByETyUaGo1EnLS+P+bbGCVHV62z46BzZYm16noDid69+4bzn0g==", + "version": "6.23.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.23.1.tgz", + "integrity": "sha512-J2Xnn5lFYT1ZN/5ewEoMBCmLlL71lZ3mBdb7cUEuHhX2ESoSrNEucpsDXpX22EuTGm9LOgC9v4Z0wx+Ez8QmGA==", "dependencies": { - "@codemirror/state": "^6.0.0", - "style-mod": "^4.0.0", + "@codemirror/state": "^6.4.0", + "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, @@ -1350,6 +1366,20 @@ } } }, + "node_modules/@sagold/json-pointer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@sagold/json-pointer/-/json-pointer-5.1.1.tgz", + "integrity": "sha512-/iskWuyGNu09qy09HYmyLnvzpKryymH9T+vTBi2LdFp1TuKvERDADvPMv2ZkQKsrRklOzivmOz9QXof0dKqvgA==" + }, + "node_modules/@sagold/json-query": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@sagold/json-query/-/json-query-6.1.1.tgz", + "integrity": "sha512-5/Wu0rTnXmO5Uvtm9Of16Vx3mKjSnYA0Um9LgBtyPhIucYlppKgKC4N3g8gD0Fk00a5kizQTs4gwxKPXCpmeww==", + "dependencies": { + "@sagold/json-pointer": "^5.1.1", + "ebnf": "^1.9.1" + } + }, "node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", @@ -2174,8 +2204,7 @@ "node_modules/@types/json-schema": { "version": "7.0.14", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", - "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", - "dev": true + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==" }, "node_modules/@types/json5": { "version": "0.0.29", @@ -3537,6 +3566,53 @@ "@codemirror/view": "^6.0.0" } }, + "node_modules/codemirror-json-schema": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/codemirror-json-schema/-/codemirror-json-schema-0.6.1.tgz", + "integrity": "sha512-QG12Jy917eStZzxurpAE9QUQxF8SS/AYJ9DDteyJZcRGH8ePaBCfQ4KLCNtY6cUKjFeNBgcd5+c6FPAri6pPQg==", + "dependencies": { + "@changesets/changelog-github": "^0.4.8", + "@sagold/json-pointer": "^5.1.1", + "@types/json-schema": "^7.0.12", + "@types/node": "^20.4.2", + "json-schema": "^0.4.0", + "json-schema-library": "^9.1.2" + }, + "optionalDependencies": { + "@codemirror/lang-json": "^6.0.1", + "codemirror-json5": "^1.0.3", + "json5": "^2.2.3" + }, + "peerDependencies": { + "@codemirror/language": "^6.8.0", + "@codemirror/lint": "^6.4.0", + "@codemirror/state": "^6.2.1", + "@codemirror/view": "^6.14.1", + "@lezer/common": "^1.0.3" + } + }, + "node_modules/codemirror-json-schema/node_modules/@types/node": { + "version": "20.11.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.10.tgz", + "integrity": "sha512-rZEfe/hJSGYmdfX9tvcPMYeYPW2sNl50nsw4jZmRcaG0HIAb0WYEpsB05GOb53vjqpyE9GUhlDQ4jLSoB5q9kg==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/codemirror-json5": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/codemirror-json5/-/codemirror-json5-1.0.3.tgz", + "integrity": "sha512-HmmoYO2huQxoaoG5ARKjqQc9mz7/qmNPvMbISVfIE2Gk1+4vZQg9X3G6g49MYM5IK00Ol3aijd7OKrySuOkA7Q==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/highlight": "^1.0.0", + "json5": "^2.2.1", + "lezer-json5": "^2.0.2" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -3724,6 +3800,11 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "node_modules/dataloader": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-1.4.0.tgz", + "integrity": "sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -3803,6 +3884,14 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/defer-to-connect": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", @@ -3885,6 +3974,11 @@ "node": ">=8" } }, + "node_modules/discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==" + }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -3924,6 +4018,14 @@ "node": ">=4" } }, + "node_modules/dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "engines": { + "node": ">=10" + } + }, "node_modules/duplexer3": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", @@ -3936,6 +4038,14 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/ebnf": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ebnf/-/ebnf-1.9.1.tgz", + "integrity": "sha512-uW2UKSsuty9ANJ3YByIQE4ANkD8nqUPO7r6Fwcc1ADKPe9FRdcPpMl3VEput4JSvKBJ4J86npIC2MLP0pYkCuw==", + "bin": { + "ebnf": "dist/bin.js" + } + }, "node_modules/electron": { "version": "23.3.13", "resolved": "https://registry.npmjs.org/electron/-/electron-23.3.13.tgz", @@ -4777,6 +4887,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/fast-copy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.1.tgz", + "integrity": "sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -6097,6 +6212,25 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-library": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/json-schema-library/-/json-schema-library-9.1.2.tgz", + "integrity": "sha512-uQnFb2V+VakLl6XIGGtUQzfjkP31f/dCT5lJq9NOUdypSSpjbWL/V0R2KvoNJp3hU8VErwh9DqVoZPqlC+B3IA==", + "dependencies": { + "@sagold/json-pointer": "^5.1.1", + "@sagold/json-query": "^6.1.1", + "deepmerge": "^4.3.1", + "fast-copy": "^3.0.1", + "fast-deep-equal": "^3.1.3", + "smtp-address-parser": "1.0.10", + "valid-url": "^1.0.9" + } + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -6120,7 +6254,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "bin": { "json5": "lib/cli.js" }, @@ -6207,6 +6340,14 @@ "node": ">= 0.8.0" } }, + "node_modules/lezer-json5": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lezer-json5/-/lezer-json5-2.0.2.tgz", + "integrity": "sha512-NRmtBlKW/f8mA7xatKq8IUOq045t8GVHI4kZXrUtYYUdiVeGiO6zKGAV7/nUAnf5q+rYTY+SWX/gvQdFXMjNxQ==", + "dependencies": { + "@lezer/lr": "^1.0.0" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -6590,6 +6731,11 @@ "ufo": "^1.3.0" } }, + "node_modules/moo": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", + "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==" + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -6654,12 +6800,57 @@ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true }, + "node_modules/nearley": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", + "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", + "dependencies": { + "commander": "^2.19.0", + "moo": "^0.5.0", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6" + }, + "bin": { + "nearley-railroad": "bin/nearley-railroad.js", + "nearley-test": "bin/nearley-test.js", + "nearley-unparse": "bin/nearley-unparse.js", + "nearleyc": "bin/nearleyc.js" + }, + "funding": { + "type": "individual", + "url": "https://nearley.js.org/#give-to-nearley" + } + }, + "node_modules/nearley/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-releases": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", @@ -7606,6 +7797,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==" + }, + "node_modules/randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "dependencies": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -8114,6 +8322,14 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "engines": { + "node": ">=0.12" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -8523,6 +8739,17 @@ "node": ">=8.0.0" } }, + "node_modules/smtp-address-parser": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/smtp-address-parser/-/smtp-address-parser-1.0.10.tgz", + "integrity": "sha512-Osg9LmvGeAG/hyao4mldbflLOkkr3a+h4m1lwKCK5U8M6ZAr7tdXEz/+/vr752TSGE4MNUlUl9cIK2cB8cgzXg==", + "dependencies": { + "nearley": "^2.20.1" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -9253,6 +9480,11 @@ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/ts-easing": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz", @@ -9448,8 +9680,7 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "devOptional": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/unique-string": { "version": "1.0.0", @@ -9578,6 +9809,11 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/valid-url": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", + "integrity": "sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==" + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -9780,6 +10016,20 @@ "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 0408d97b..3ff30261 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,8 @@ "classnames": "^2.3.2", "cm6-graphql": "^0.0.9", "codemirror": "^6.0.1", + "codemirror-json-schema": "^0.6.1", + "codemirror-json5": "^1.0.3", "focus-trap-react": "^10.1.1", "format-graphql": "^1.4.0", "framer-motion": "^9.0.4", diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 92d69ec6..b504ab63 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -84,7 +84,11 @@ async fn grpc_reflect( // app_handle: AppHandle, // db_instance: State<'_, Mutex>>, ) -> Result, String> { - let uri = Uri::from_str(endpoint).map_err(|e| e.to_string())?; + let uri = if endpoint.starts_with("http://") || endpoint.starts_with("https://") { + Uri::from_str(endpoint).map_err(|e| e.to_string())? + } else { + Uri::from_str(&format!("http://{}", endpoint)).map_err(|e| e.to_string())? + }; Ok(grpc::callable(&uri).await) } @@ -97,7 +101,11 @@ async fn grpc_call_unary( // app_handle: AppHandle, // db_instance: State<'_, Mutex>>, ) -> Result { - let uri = Uri::from_str(endpoint).map_err(|e| e.to_string())?; + let uri = if endpoint.starts_with("http://") || endpoint.starts_with("https://") { + Uri::from_str(endpoint).map_err(|e| e.to_string())? + } else { + Uri::from_str(&format!("http://{}", endpoint)).map_err(|e| e.to_string())? + }; Ok(grpc::call(&uri, service, method, message).await) } diff --git a/src-tauri/src/models.rs b/src-tauri/src/models.rs index d33fd3ce..cac82da4 100644 --- a/src-tauri/src/models.rs +++ b/src-tauri/src/models.rs @@ -95,6 +95,19 @@ pub struct EnvironmentVariable { pub value: String, } +#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Default)] +#[serde(default, rename_all = "camelCase")] +pub struct Folder { + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, + pub id: String, + pub workspace_id: String, + pub folder_id: Option, + pub model: String, + pub name: String, + pub sort_priority: f64, +} + #[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(default, rename_all = "camelCase")] pub struct HttpRequestHeader { @@ -139,19 +152,6 @@ pub struct HttpRequest { pub headers: Json>, } -#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Default)] -#[serde(default, rename_all = "camelCase")] -pub struct Folder { - pub created_at: NaiveDateTime, - pub updated_at: NaiveDateTime, - pub id: String, - pub workspace_id: String, - pub folder_id: Option, - pub model: String, - pub name: String, - pub sort_priority: f64, -} - #[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(default, rename_all = "camelCase")] pub struct HttpResponseHeader { @@ -190,6 +190,64 @@ impl HttpResponse { } } +#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Default)] +#[serde(default, rename_all = "camelCase")] +pub struct GrpcEndpoint { + pub id: String, + pub model: String, + pub workspace_id: String, + pub request_id: String, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, + pub endpoint: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(default, rename_all = "camelCase")] +pub struct GrpcMessage { + pub created_at: NaiveDateTime, + pub content: String, +} + +#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Default)] +#[serde(default, rename_all = "camelCase")] +pub struct GrpcConnection { + pub id: String, + pub model: String, + pub workspace_id: String, + pub grpc_endpoint_id: String, + pub request_id: String, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, + pub messages: Json>, +} + +#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Default)] +#[serde(default, rename_all = "camelCase")] +pub struct GrpcRequest { + pub id: String, + pub model: String, + pub workspace_id: String, + pub grpc_endpoint_id: String, + pub grpc_connection_id: String, + pub request_id: String, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, +} + +#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Default)] +#[serde(default, rename_all = "camelCase")] +pub struct GrpcResponse { + pub id: String, + pub model: String, + pub workspace_id: String, + pub grpc_endpoint_id: String, + pub grpc_connection_id: String, + pub request_id: String, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, +} + #[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Default)] #[serde(default, rename_all = "camelCase")] pub struct KeyValue { diff --git a/src-web/components/GraphQLEditor.tsx b/src-web/components/GraphQLEditor.tsx index 31e89bb8..6f17dda8 100644 --- a/src-web/components/GraphQLEditor.tsx +++ b/src-web/components/GraphQLEditor.tsx @@ -72,7 +72,7 @@ export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEdi const dialog = useDialog(); return ( -
+
-

Variables

+

Variables

({ namespace: 'debug', key: 'grpc_url', defaultValue: '' }); + const message = useKeyValue({ + namespace: 'debug', + key: 'grpc_message', + defaultValue: '', + }); + const [resp, setResp] = useState(''); + const grpc = useGrpc(url.value ?? null); + const handleConnect = useCallback( + async (e: FormEvent) => { + e.preventDefault(); + setResp( + await grpc.callUnary.mutateAsync({ + service: 'helloworld.Greeter', + method: 'SayHello', + message: message.value ?? '', + }), + ); + }, + [grpc.callUnary, message.value], + ); + + useEffect(() => { + console.log('REFLECT SCHEMA', grpc.schema); + }, [grpc.schema]); + + if (url.isLoading || url.value == null) { + return null; + } + + return ( + ( + + + + + )} + rightSlot={() => ( + + )} + /> + ); +} diff --git a/src-web/components/GrpcEditor.tsx b/src-web/components/GrpcEditor.tsx new file mode 100644 index 00000000..362f71d6 --- /dev/null +++ b/src-web/components/GrpcEditor.tsx @@ -0,0 +1,40 @@ +import type { EditorView } from 'codemirror'; +import { updateSchema } from 'codemirror-json-schema'; +import { useEffect, useRef } from 'react'; +import { useGrpc } from '../hooks/useGrpc'; +import { tryFormatJson } from '../lib/formatters'; +import type { EditorProps } from './core/Editor'; +import { Editor } from './core/Editor'; + +type Props = Pick< + EditorProps, + 'heightMode' | 'onChange' | 'defaultValue' | 'className' | 'forceUpdateKey' +> & { + url: string; +}; + +export function GrpcEditor({ url, defaultValue, ...extraEditorProps }: Props) { + const editorViewRef = useRef(null); + const { schema } = useGrpc(url); + + useEffect(() => { + if (editorViewRef.current == null || schema == null) return; + const foo = schema[0].methods[0].schema; + console.log('UPDATE SCHEMA', foo); + updateSchema(editorViewRef.current, JSON.parse(foo)); + }, [schema]); + + return ( +
+ +
+ ); +} diff --git a/src-web/components/HttpRequestLayout.tsx b/src-web/components/HttpRequestLayout.tsx new file mode 100644 index 00000000..64287bed --- /dev/null +++ b/src-web/components/HttpRequestLayout.tsx @@ -0,0 +1,21 @@ +import type { CSSProperties } from 'react'; +import React from 'react'; +import { SplitLayout } from './core/SplitLayout'; +import { RequestPane } from './RequestPane'; +import { ResponsePane } from './ResponsePane'; + +interface Props { + style: CSSProperties; +} + +export function HttpRequestLayout({ style }: Props) { + return ( + ( + + )} + rightSlot={({ style }) => } + /> + ); +} diff --git a/src-web/components/RequestPane.tsx b/src-web/components/RequestPane.tsx index 5b6d601e..ae899af4 100644 --- a/src-web/components/RequestPane.tsx +++ b/src-web/components/RequestPane.tsx @@ -1,9 +1,11 @@ import classNames from 'classnames'; -import type { CSSProperties } from 'react'; +import type { CSSProperties, FormEvent } from 'react'; import { memo, useCallback, useMemo, useState } from 'react'; import { createGlobalState } from 'react-use'; import { useActiveRequest } from '../hooks/useActiveRequest'; +import { useIsResponseLoading } from '../hooks/useIsResponseLoading'; import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey'; +import { useSendRequest } from '../hooks/useSendRequest'; import { useUpdateRequest } from '../hooks/useUpdateRequest'; import { tryFormatJson } from '../lib/formatters'; import type { HttpHeader, HttpRequest, HttpUrlParameter } from '../lib/models'; @@ -33,7 +35,7 @@ import { UrlBar } from './UrlBar'; import { UrlParametersEditor } from './UrlParameterEditor'; interface Props { - style?: CSSProperties; + style: CSSProperties; fullHeight: boolean; className?: string; } @@ -178,6 +180,27 @@ export const RequestPane = memo(function RequestPane({ style, fullHeight, classN [updateRequest], ); + const sendRequest = useSendRequest(activeRequest?.id ?? null); + const handleSend = useCallback( + async (e: FormEvent) => { + e.preventDefault(); + await sendRequest.mutateAsync(); + }, + [sendRequest], + ); + + const handleMethodChange = useCallback( + (method: string) => updateRequest.mutate({ method }), + [updateRequest], + ); + const handleUrlChange = useCallback( + (url: string) => updateRequest.mutate({ url }), + [updateRequest], + ); + + const isLoading = useIsResponseLoading(activeRequestId ?? null); + const { updateKey } = useRequestUpdateKey(activeRequestId ?? null); + return (
& { +type Props = Pick & { className?: string; + method: HttpRequest['method'] | null; + placeholder: string; + onSubmit: (e: FormEvent) => void; + onUrlChange: (url: string) => void; + onMethodChange?: (method: string) => void; + isLoading: boolean; + forceUpdateKey: string; }; -export const UrlBar = memo(function UrlBar({ id: requestId, url, method, className }: Props) { +export const UrlBar = memo(function UrlBar({ + forceUpdateKey, + onUrlChange, + url, + method, + placeholder, + className, + onSubmit, + onMethodChange, + isLoading, +}: Props) { const inputRef = useRef(null); - const sendRequest = useSendRequest(requestId); - const updateRequest = useUpdateRequest(requestId); const [isFocused, setIsFocused] = useState(false); - const handleMethodChange = useCallback( - (method: string) => updateRequest.mutate({ method }), - [updateRequest], - ); - const handleUrlChange = useCallback( - (url: string) => updateRequest.mutate({ url }), - [updateRequest], - ); - const loading = useIsResponseLoading(requestId); - const { updateKey } = useRequestUpdateKey(requestId); - - const handleSubmit = useCallback( - async (e: FormEvent) => { - e.preventDefault(); - sendRequest.mutate(); - }, - [sendRequest], - ); useHotKey('urlBar.focus', () => { const head = inputRef.current?.state.doc.length ?? 0; @@ -48,7 +41,7 @@ export const UrlBar = memo(function UrlBar({ id: requestId, url, method, classNa }); return ( -
+ setIsFocused(true)} onBlur={() => setIsFocused(false)} containerClassName="shadow shadow-gray-100 dark:shadow-gray-50" - onChange={handleUrlChange} + onChange={onUrlChange} defaultValue={url} - placeholder="https://example.com" + placeholder={placeholder} leftSlot={ - + method != null && + onMethodChange != null && ( + + ) } rightSlot={ } diff --git a/src-web/components/Workspace.tsx b/src-web/components/Workspace.tsx index 6b6a0e6a..975beb9d 100644 --- a/src-web/components/Workspace.tsx +++ b/src-web/components/Workspace.tsx @@ -8,14 +8,16 @@ import type { } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useWindowSize } from 'react-use'; +import { useActiveRequest } from '../hooks/useActiveRequest'; import { useIsFullscreen } from '../hooks/useIsFullscreen'; import { useOsInfo } from '../hooks/useOsInfo'; import { useSidebarHidden } from '../hooks/useSidebarHidden'; import { useSidebarWidth } from '../hooks/useSidebarWidth'; import { Button } from './core/Button'; import { HStack } from './core/Stacks'; +import { GrpcConnectionLayout } from './GrpcConnectionLayout'; +import { HttpRequestLayout } from './HttpRequestLayout'; import { Overlay } from './Overlay'; -import { RequestResponse } from './RequestResponse'; import { ResizeHandle } from './ResizeHandle'; import { Sidebar } from './Sidebar'; import { SidebarActions } from './SidebarActions'; @@ -31,6 +33,7 @@ const WINDOW_FLOATING_SIDEBAR_WIDTH = 600; export default function Workspace() { const { setWidth, width, resetWidth } = useSidebarWidth(); const { hide, show, hidden } = useSidebarHidden(); + const activeRequest = useActiveRequest(); const windowSize = useWindowSize(); const [floating, setFloating] = useState(false); @@ -163,7 +166,11 @@ export default function Workspace() { > - + {activeRequest?.name.includes('gRPC') ? ( + + ) : ( + + )}
); } diff --git a/src-web/components/core/Editor/extensions.ts b/src-web/components/core/Editor/extensions.ts index f540bf55..e67052ac 100644 --- a/src-web/components/core/Editor/extensions.ts +++ b/src-web/components/core/Editor/extensions.ts @@ -10,7 +10,6 @@ import { json } from '@codemirror/lang-json'; import { xml } from '@codemirror/lang-xml'; import type { LanguageSupport } from '@codemirror/language'; import { - bracketMatching, foldGutter, foldKeymap, HighlightStyle, @@ -32,6 +31,7 @@ import { } from '@codemirror/view'; import { tags as t } from '@lezer/highlight'; import { graphql, graphqlLanguageSupport } from 'cm6-graphql'; +import { jsonSchema } from 'codemirror-json-schema'; import type { Environment, Workspace } from '../../../lib/models'; import type { EditorProps } from './index'; import { text } from './text/extension'; @@ -83,6 +83,7 @@ export const myHighlightStyle = HighlightStyle.define([ // ]); const syntaxExtensions: Record = { + 'application/grpc': jsonSchema() as any, // TODO: Fix this 'application/graphql': graphqlLanguageSupport(), 'application/json': json(), 'application/javascript': javascript(), @@ -119,7 +120,6 @@ export const baseExtensions = [ history(), dropCursor(), drawSelection(), - bracketMatching(), // TODO: Figure out how to debounce showing of autocomplete in a good way // debouncedAutocompletionDisplay({ millis: 1000 }), // autocompletion({ closeOnBlur: true, interactionDelay: 200, activateOnTyping: false }), diff --git a/src-web/components/RequestResponse.tsx b/src-web/components/core/SplitLayout.tsx similarity index 74% rename from src-web/components/RequestResponse.tsx rename to src-web/components/core/SplitLayout.tsx index 0d961fbe..bcb57aed 100644 --- a/src-web/components/RequestResponse.tsx +++ b/src-web/components/core/SplitLayout.tsx @@ -1,32 +1,36 @@ import useResizeObserver from '@react-hook/resize-observer'; import classNames from 'classnames'; -import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react'; -import React, { memo, useCallback, useMemo, useRef, useState } from 'react'; +import type { CSSProperties, MouseEvent as ReactMouseEvent, ReactNode } from 'react'; +import React, { useCallback, useMemo, useRef, useState } from 'react'; import { useLocalStorage } from 'react-use'; -import { useActiveRequest } from '../hooks/useActiveRequest'; -import { useActiveWorkspaceId } from '../hooks/useActiveWorkspaceId'; -import { clamp } from '../lib/clamp'; -import { HotKeyList } from './core/HotKeyList'; -import { RequestPane } from './RequestPane'; -import { ResizeHandle } from './ResizeHandle'; -import { ResponsePane } from './ResponsePane'; +import { useActiveRequestId } from '../../hooks/useActiveRequestId'; +import { useActiveWorkspaceId } from '../../hooks/useActiveWorkspaceId'; +import { clamp } from '../../lib/clamp'; +import { ResizeHandle } from '../ResizeHandle'; +import { HotKeyList } from './HotKeyList'; -interface Props { +interface SlotProps { + orientation: 'horizontal' | 'vertical'; style: CSSProperties; } -const rqst = { gridArea: 'rqst' }; -const resp = { gridArea: 'resp' }; -const drag = { gridArea: 'drag' }; +interface Props { + style: CSSProperties; + leftSlot: (props: SlotProps) => ReactNode; + rightSlot: (props: SlotProps) => ReactNode; +} + +const areaL = { gridArea: 'left' }; +const areaR = { gridArea: 'right' }; +const areaD = { gridArea: 'drag' }; const DEFAULT = 0.5; const MIN_WIDTH_PX = 10; const MIN_HEIGHT_PX = 30; const STACK_VERTICAL_WIDTH = 700; -export const RequestResponse = memo(function RequestResponse({ style }: Props) { +export function SplitLayout({ style, leftSlot, rightSlot }: Props) { const containerRef = useRef(null); - const activeRequest = useActiveRequest(); const [vertical, setVertical] = useState(false); const [widthRaw, setWidth] = useLocalStorage(`body_width::${useActiveWorkspaceId()}`); const [heightRaw, setHeight] = useLocalStorage(`body_height::${useActiveWorkspaceId()}`); @@ -46,13 +50,13 @@ export const RequestResponse = memo(function RequestResponse({ style }: Props) { ...style, gridTemplate: vertical ? ` - ' ${rqst.gridArea}' minmax(0,${1 - height}fr) - ' ${drag.gridArea}' 0 - ' ${resp.gridArea}' minmax(0,${height}fr) + ' ${areaL.gridArea}' minmax(0,${1 - height}fr) + ' ${areaD.gridArea}' 0 + ' ${areaR.gridArea}' minmax(0,${height}fr) / 1fr ` : ` - ' ${rqst.gridArea} ${drag.gridArea} ${resp.gridArea}' minmax(0,1fr) + ' ${areaL.gridArea} ${areaD.gridArea} ${areaR.gridArea}' minmax(0,1fr) / ${1 - width}fr 0 ${width}fr `, }), @@ -117,15 +121,16 @@ export const RequestResponse = memo(function RequestResponse({ style }: Props) { [width, height, vertical, setHeight, setWidth], ); - if (activeRequest === null) { + const activeRequestId = useActiveRequestId(); + if (activeRequestId === null) { return ; } return (
- + {leftSlot({ style: areaL, orientation: vertical ? 'vertical' : 'horizontal' })} - + {rightSlot({ style: areaR, orientation: vertical ? 'vertical' : 'horizontal' })}
); -}); +} diff --git a/src-web/hooks/useCreateCookieJar.ts b/src-web/hooks/useCreateCookieJar.ts index 9f2c70be..8edb32d2 100644 --- a/src-web/hooks/useCreateCookieJar.ts +++ b/src-web/hooks/useCreateCookieJar.ts @@ -1,17 +1,17 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { invoke } from '@tauri-apps/api'; import { trackEvent } from '../lib/analytics'; -import type { HttpRequest } from '../lib/models'; +import type { CookieJar } from '../lib/models'; import { useActiveWorkspaceId } from './useActiveWorkspaceId'; +import { cookieJarsQueryKey } from './useCookieJars'; import { usePrompt } from './usePrompt'; -import { requestsQueryKey } from './useRequests'; export function useCreateCookieJar() { const workspaceId = useActiveWorkspaceId(); const queryClient = useQueryClient(); const prompt = usePrompt(); - return useMutation({ + return useMutation({ mutationFn: async () => { if (workspaceId === null) { throw new Error("Cannot create cookie jar when there's no active workspace"); @@ -26,10 +26,10 @@ export function useCreateCookieJar() { return invoke('create_cookie_jar', { workspaceId, name }); }, onSettled: () => trackEvent('CookieJar', 'Create'), - onSuccess: async (request) => { - queryClient.setQueryData( - requestsQueryKey({ workspaceId: request.workspaceId }), - (requests) => [...(requests ?? []), request], + onSuccess: async (cookieJar) => { + queryClient.setQueryData( + cookieJarsQueryKey({ workspaceId: cookieJar.workspaceId }), + (items) => [...(items ?? []), cookieJar], ); }, }); diff --git a/src-web/hooks/useGrpc.ts b/src-web/hooks/useGrpc.ts new file mode 100644 index 00000000..55006b1f --- /dev/null +++ b/src-web/hooks/useGrpc.ts @@ -0,0 +1,35 @@ +import { useMutation, useQuery } from '@tanstack/react-query'; +import { invoke } from '@tauri-apps/api'; + +export function useGrpc(url: string | null) { + const callUnary = useMutation< + string, + unknown, + { service: string; method: string; message: string } + >({ + mutationKey: ['grpc_call_reflect', url], + mutationFn: async ({ service, method, message }) => { + if (url === null) throw new Error('No URL provided'); + return (await invoke('grpc_call_unary', { + endpoint: url, + service, + method, + message, + })) as string; + }, + }); + + const reflect = useQuery({ + queryKey: ['grpc_reflect', url ?? ''], + queryFn: async () => { + if (url === null) return null; + console.log('GETTING SCHEMA', url); + return (await invoke('grpc_reflect', { endpoint: url })) as string; + }, + }); + + return { + callUnary, + schema: reflect.data, + }; +} diff --git a/src-web/main.tsx b/src-web/main.tsx index f2a7491a..b53972f7 100644 --- a/src-web/main.tsx +++ b/src-web/main.tsx @@ -11,20 +11,6 @@ import { setAppearanceOnDocument } from './lib/theme/window'; import { appWindow } from '@tauri-apps/api/window'; import { type } from '@tauri-apps/api/os'; -try { - const services: any = await invoke('grpc_reflect', { endpoint: 'http://localhost:50051' }); - console.log('SERVICES', services); - const response = await invoke('grpc_call_unary', { - endpoint: 'http://localhost:50051', - service: services[0].name, - method: services[0].methods[0].name, - message: '{"name": "Greg"}', - }); - console.log('RESPONSE', response); -} catch (err) { - console.log('ERROR', err); -} - // Hide decorations here because it doesn't work in Rust for some reason (bug?) const osType = await type(); if (osType !== 'Darwin') {