From 9022f33802427d1d5271fe560d6f304983eb0f12 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Tue, 10 Sep 2024 08:22:45 +0000 Subject: [PATCH 01/15] feat updated WhereClause type to include QueryParameter Signed-off-by: Jim Ezesinachi --- .../static/reference/assets/navigation.js | 2 +- .../docs/static/reference/assets/search.js | 2 +- packages/queries/src/query.test.ts | 38 +++++++++++++------ packages/queries/src/types/AnyQuery.ts | 2 + packages/queries/src/types/BinaryOp.ts | 3 +- packages/queries/src/types/QueryParameter.ts | 2 +- packages/queries/src/types/Where.ts | 16 ++++++-- packages/queries/src/types/WhereClause.ts | 6 ++- 8 files changed, 50 insertions(+), 21 deletions(-) diff --git a/packages/docs/static/reference/assets/navigation.js b/packages/docs/static/reference/assets/navigation.js index abb0d21d..a9e347be 100644 --- a/packages/docs/static/reference/assets/navigation.js +++ b/packages/docs/static/reference/assets/navigation.js @@ -1 +1 @@ -window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAA5WXbW/bIBCA/4s/p+vard2Wb2kaad2mNkuiTVNVVRRfGlQCDuAq1rT/Phk78Qv4oF/DwwMcx+V8/zcxsDfJOHki9AVEeqoVTUZJRswmGSdbmeYc9Gk9+KgVfbcxW56Mkhcm0mR8PkrohvFUgUjG90fZzxxUMRPPTEAjo5xo3ZO1wK747Pzzv9HRtyyE2ez4TCmpcGGbxIythedKZrqxMmFArQkd3qmd0IvDxWVfPudExFlLEtNRyTlQ84No0wjXuaCGSdH1tdCu8fJjR7jNpAa7eNjYsI7yoS3lzJ88lDNP4rRmbohIOagT2GcKtPZbauixhqJTkSogBmbVrDo7vlYu39F9yyAKNCKHcwnYG/xQJRF9olvYm6GDmCLzad0pvZXef/l0dtG+E3fGAnY5tDMweq16ZmjJKszY6dxrOq46NBm9ICaMkjoDOnA9zXj05TyDAEUM+Dbd0x1QdIu7HBSDgTdRD0Zvbip5vhXXsPbWpbbtSGJ16QiVBRGUYeAvo15xMydqiTeYUeGSbmBLgrIKw0Qr8sQhJpQHEJNNRHF91X9ZbYkFQg9oIopeSfeKPKXc67IbD7gsE+vqRGtY58bKMV4xQVRxl2G6AxPrKt+idEqpz1iRIe+UqJQJwplBL6SFBY02w1GZJeI8qyJDb7eh4ny/CM8jhBYLGW8E5XmK2mokZPommcDzpCJCnuDDinpVVbtHFNmCcf+2Hd2RjPIuQOfc+X92pBUWMi5gjYfNAiHLEspuFNNURMgTLEVRdej3BhRqsUCUZcpJrsOuCgtWoJvbyeLP4918tpis7hbLRvtKFCtP1qtDPb7r/9DpqST3dSJtG5Uc+VRgeihn/bY+j6p7STZk9KRaR5SVi4U0FkIku6GPobbEQidnaMemgAz1k3YoulurG9mpFPYHX5/RCLsw2gFVpL2m7+DUNEd5AENZrAe22+Rwo+6y/YB2MrjexVzJV5b688/Z8gFGk28wDj6xgyNq/RZxD/bkViPONdS7wJ0NFyVzbgx3elPMvoGH/wmqzOxOEgAA" \ No newline at end of file +window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE5WXbW/bIBCA/4s/p+vard2Wb2kaad2mNkuiTVNVVRRfalQCDuAq1rT/PvklfgMf9Gt4eIDjOF/u/0YGDiaaRk+EvoCIT7Wi0SRKiUmiabSTccZBn9aDj1rRd4nZ8WgSvTARR9PzSUQTxmMFIpreN7KfGah8IZ6ZgFZGOdF6IOuAffHZ+ed/k8a3zoVJ9nyhlFS4sEtixs7CSyVT3VqZMKC2hI7vtJwwiMPF5VC+5ESEWQsS01HJOVDzg2jTCreZoIZJ0fd10L7x8mNPuEulhnJxv7FlLeVDV8qZO3koZ47E6cxMiIg5qBM4pAq0dltq6LGGglORKiAGFtWsOju+Vi7X0V3LIAo0IsdzCTgY/FAFEXyiWziYsYOYPHVp7SmDld5/+XR20b0Te8YK9hl0MzB4rXqmb8kqzNjp7GtqVh2bjF4QE0ZJnQIduZ52PPhynkGAIgZcmx7ojii6xX0GisHIm6gHgzc3lzzbiWvYOutS19aQWF1qoKIggjIM3GXUKW7nBC3xBjMqXNMEdsQrqzBMtCFPHEJCeQQx2Uzk1faHr6sraiDfQ5qJ/PrKY7q+CrAMPhBOkePD4HSVYfC4SibU1Yv9uM6OvGW8YoKo/C7FdEcm1FW8bGkVZpexIn3eOVExE4Qzg15IB/MavQkXlm0VtclT9HZbKsz3i/AsQFhiPuONoDyLUVuN+EzfJBN4nlSEz+N9WEGvqmoeiSI7MHYTYOkaMsi7Ap1x62tvSSvMZ1zBFg9bCfgsayh6W0xTET6PtxQF1aHfCSjUUgJBljknmfa7KsxbgW5uZ6s/j3fLxWq2uVutW+0rUaw42aAODfi+/0OvQ5Pc1dd0bVRy5I8H02M567YNeVQ9SLIxoyPVeqK0WMynKSFEsh/7a9WVlNDJGdr/KSBj3Wk5FNz71W3xXIryB1fX0gr7MNpPVWR5Td/BqmmW8gj6sliPbLfN4VbdZ4cB7WVwvYulkq8sduefteUjjCbfaBxcYgtH1Pot4gHsyK1WnGmod4E7Wy5IZt0Y7nSmWPkGHv4DDtAzbZwSAAA=" \ No newline at end of file diff --git a/packages/docs/static/reference/assets/search.js b/packages/docs/static/reference/assets/search.js index 7687d576..bf49072c 100644 --- a/packages/docs/static/reference/assets/search.js +++ b/packages/docs/static/reference/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,H4sIAAAAAAAAA71dbY/bNhL+KwdtPzquqXftt+YFuPYObS4JWhwWwUKxubtqZdkryWn2gvz3A6m3GXpGb9bmy22u5sw85DMzJIeS+NXKD38X1vXNV+uvJNtZ1/bKyuK9tK6tT/H2L5ntfizyrbWyTnlqXVv7w+6UyuLH+rfbIt+uH8p9aq2sbRoXhSysa8v6tjrXtk0TUtM2TSZoeYizXSrzF/LLMZdFQWqs29zWbWZoz+SXsle1ajBBb5KV+aE4yi2ttft5gs7Hk8wTSY9A/dsEbbmMGXD6l0FNvtsRfUhTuS3/HRdlq+zulG3L5JBhxwEtCeUr6xjnMisNT2Rs7o+HQv7nJPOnQaNd01lWPc/xW8O3t+XTUU41eYX+S6tjEAbWhDrdARQbuxuY4jF9eUrSncyXwLhG6i6A2/SZQR2f7vcyK+VuFqEk8jOVi6K3vc4ldJO3aZy1uJOslPldvDXyZttwjhui8XrzRW5PamT+8SHZy2l2r1rh21p4eGS6LjKA1G9Zkt3PwdPILgjnvSzLJLsvJiIBYvNA9OaKaRimZIlORdsDZmA+5Mn9vcynDgwQW9BZZvrJciBmAJht/CxjvMnuk0y+zQ/HcWyA9hfnD2Vtjs2rSnDkCMAectPV9kHu43lYWtnl4BxzeZTZ7v3jzOFB8kvCOnxOdmPDlkDViS8I6nCYO0qV5HJQ0sP9/dh8coamE74MkB1S8d1iqpVzgOaEtCfAbuuQFWV+2paHfJLJKyw4aQhGecYoEGceURa7F0nx4pgnn+NSXoDFSDCj0BCJZTE8RIYZN0J0ZlkMl9SLwgNIMaNgQbFlUNkbNzRQTQuiq05orjdTGH7Kdn/ESTkLSie7CCK1NUnSiaPSCc3F0Lu4nYJhXf99ISYvcmtthqKezfAyANfF9BndBDmwBT7GebyfGH0s3FbZwoiNwDimcZJNjYhGaPZUAyba909Z+fCYvsnzgWkPNpwz1eL4y2VcytdxGX+KC/nqkGVSlylmwLga0jU8SmgMehG/f0zbvf98rJSWJVG+bWe6BcD2KFsS8ztZHA9ZId+XuYz3SXY/HzGrakm8vxSH7G2cFxchJZQsifFVnO+SLE6T8mk+RkLJbIxzltsEronrbQYMXrzlU1FUEstYH1wHnJsfPemPsb+XRRHfT4XQSZ2tW5PsQeZJKXeXoNoedlMh1SKzRwWej2jvf1MdiNXN/1mdZRHVdeoArUdD/wRKndjBQI8C4XWB9Kv8UjL4lJMQJ3DnEuPwtGd8I1e008xfDaxkTRSEnkmj9E4+niQ4cBuNthacP2amk/UweO5hLTBOdglc9zKTeVxSp3TGyWvTst+qcZrLHYIS5uC57PaQ9puBB7ykDb2qH7Ci21xo55E5hIN2dJsXYpalcQeqnLWrxzGHqMB0JV39L7fkuMsPQyPLYljXwpOAUHsrHPP4KLSKb4hnxIH2jPHvszOU42BXew+p0WiPsDh+hMccNhdSPYQwyX4rsgyCvx9kPmnI143EMvaTbJuedtMQdDLLYEiTfTKNhEZiGfuHu7tCTgPQiiyDYNvtTSbBwHILsRH/bxqIWmAZ6/f54XR8OQ1AJ7MMhoe4eJgEoBZYxrr+M8V6LTDbujnb/JQ9fYg/pfK1vOvBAVp9h5nHtDZl/oH9YXdo6WmfFdMBrDvJSUBGsfD6ZT+g1y/njDxH94jOL2RuaDnTNJljzuksvfz515/e/ff2t7dv3v304bd371uLn+M8Ud3BVs3mS3T2ZZLF+dNvR7WzOJjbWWQcNVzS9AijS5h7NWoGA60WMaqDr8+ebrCcqd/j9NQXKKDVckY/9KfGrtESJn8eXA/WLZYw9sshyXo9tGqwhKnqOSy1I5blWWHpbH5tG36vTRU2OHl31XVsTGF2Cob1TCBD247dPDRabmEsnweCugdOI3o5ItJf38nilA5uTKpWSwTJO3nXG4769+8QEp2dKZFQoWdI/iHvXc2eWVzfVgIjzZ4TemmXNYBa7QsxGclad5jLBgPrvQEw60Z+BqShjfDQjD4ErVWwPLbDuNCgcWnhRTDBp4ffs8/MQmxVqzlxi0OIf0KXsXZ1O/BgLux63ZcxE9gIyxPCpt/wThbbPDmqIvAU+0BsERjH/HCUeZlI8sFfDkUntQiIXD6eklzupkBoZBYBEO92iRrSOH07azwo+UWA/bCTd5OQ3NYCMwyPfbOk1/SUQ5NauOrj8+BY394m2U5+mTbhQWCGohELz0shjl2M9qAcmHEWHdHxK/n5gGcmy37cWOlzwp+XZPvRI53PCX5Ocu6HDjQ+J/C7Q76Py+Vgt/qeE/SlM1F/Fxjtz5prkjJdMtnU6p4VcvFh2QTZ6HtO0PskS/an/XKoO4XPCjv+sjDsVuFzwpbZkphrbQsDhhu6s4M3BvMlR2+Tlxj44Gv0MmLo0G3CYgEjmLogGAIyftrHOCZO7UMwxk7gGMSkSXoIwpxZDcOZPXMR0GBgVKccr0fssrqWFweHeSw8zuLVlDNh0K/LdlYMiCn7PKCi6cIFWaMf0PjdyDmqxVbxAxAnxvdkoGMDfgDmpAwwGeSclDAAeHaOGAOeTBqjAX+X+RRbGz+hdt25fEY1MEydUgehzAjCWZPqIJDJQTZjWh0EcVEQXTixUuDIIJkBrRP5voFj2J0RQqCzvc8Go5OyqbCQikXBVY/gXgQOqVgUnH5C+SJsUMOi0LJTml6EDChYltDibZ7s4/zpX/JpPqVYyaUAzccD3g89LV81WOJJhKEnEhd7HPGPgefv9e+LGXqVxqdi0FzV6sI3d5KCeeSJfqXFbH6xdfwsCWd0/hMl4AtWhaxfHCPMdZ+w7Jr12+s+h0laKyoderRgrFImjbZD70gNWE6ady9H2T5rPd32Wfwz5is37kxfbBh0utb1tv5uVW+XjbYX2e285dUhK+UX6numlG/VrWcMNnysg7YMpoOzTs+2a5RLd8dDkk03eQUkB2wb4n3rdlmUP2fJDDRYeDYg8DB5QVPSPUp+FvV10wlB/3FlVWfX11+tzzIv1Gbs2rLXzjqyVtZdItOd+lpz84bI9rBXnzW1Pta//S63+vNL1zdVkx831upms3KDtR+FHz+ubhoJ/YP+D7qZsFY3gmomUDPbWt3YK2ezdoS9claOs47CALW3UXsHtnep9g5q71qrG4+C4aJmnrW68almHmrmW6ubgGrmo2aBtboJqWYBahZaq5uIahaiZpEayc3K9tae8FC7CI+4IkDQY25wo8mxyZaYHqFGXzhkS0yMUOMuGkZWwqtJwjKYHKFIaFuuhE/KYKaEYkSQJAhMlvDZgROYL6F4ESRjAlMmFDXCX7n22nMFbolZE5o2kl6BebMVOTYZUjbmzVbk2CTDthFWOq7sleOv/Y2PW2LebB1RDtUjG7NlKyJsl7SOObIVEbZH6sQc2YoIm4w8G3NkKyJskncbc2QrIuyQtI45siMeJ+bI2bDj6WCOHMGOp4M5cjRHpIc4RtJTRDikhziYI0cR4ZAe4mCOHEWEQ+YAB3Pk8HHkYI4cRYRDZgsHc+QoIhzSlxzMkaOIcMgU7mCOXEWEQ/qSizlyFREO6Usu5shVRDhkZnAxR67miGTTNaYkRYRLsulijlxFhEuy6WKOXEWES7LpYo7cgI0OF3PkKiJckk0Xc+QqIlxXZW8R4uhwMUeeIsIl2fQwR54iwqXnZMyRp4hwSTY9zJGniHBJNj3MkeeyPu8ZKwfNEcm7hznyFBEeybuHOfIUER7Ju4c58hQRHsm7hznyFBEeyaaHOfIVEZ5LeYiPOfIFO0o+5shXRHhkpvUxR77DW8cc+TxHPubI93jrxgJPc0TO7z7myNccBWRLzJGvOSIjzscc+Zoj0pd8zFGgiPBJXwowR4EiwheU9QBzFCgifNKXAsxRoIjwSV8KMEeBIsIn83yAOQr0EpzMDAHmKFBE+GRmCIx1eMB6SIA5ChQRPslmgDkKFBE+vbrHHIWaI3qBjzkKFREByWaIOQoVEQGZGULMUaiICEg2Q8xRqIgISDZDzFHosb4UYo5CvVMiozjEHIWKiIDkPTS2S4qIgOQ9xByFERtxIeYoUkQE5NwRYY4izRHJe4Q5imx+w4Y5ihx23owwR5HmKKJ6FGGOIkVESPpShDmKFBEh6UsR5ijSG1rSlyLMURTyfTd2tYqIkPS6yNzXbviN3MbY2W4UFyHpeNVvsK3NOmn1G2yr+AhJN61+g21ddrNQ/Qbbej1tjf3sxmfdpfoNttWU+bReY0+7UcyEwcpx157ZNWNTu9GshWRTgzVdcwgjqulZOUIRE5E+K8yChOCjS5gliaomwbQ1SBN8hAmzBKELDQwRZhFClxoYIswyhC420ESYdQhdbaCJMAsRutwQ0XUhoxQhbJ40oxYhdMUhootItllFUsRETBnJIE1XHSJyySCMioTQdYeIjkqjJiF05SEipw9hVCWErj1EdJHJqEsIXX1gnMGoTAhdf2CcwahNCF2BoJ3BKE4IXYKgncGoTghdg6AJNsoTQhchIroq5pjFP4ddPwmjRCEcft0ujCKFcHoizShTCF2MoAfMqFMIXY1gBsygTJcjmAEzGNP1iIgu+Rm1CqErEkzHjGqF0DUJxmuMeoXQVQl6EIyChdBlCXoQXLNO67KDYJQshC5MiA2dzI2qhXB7ZjSjbiHcajFP5zGjdCHcah1CZyejeiHcqsJOpyejgCG8ajFC5yejhiG8aoNMJyijjCF0sYLeewqjkCF0uYJZ5hilDKELFvROVXhmcb1ij86SRj1DeFVhkKnFG/R5FX10OjGqGsKr6KNDyShsCF2+EGJDd9Cgz68OSWgvMsoboqpvMJqNCofQdQzBnKoYRQ6hSxmCOVgx6hyiKnQImm2j1CF0QUMI2uV884REMyhouo2Ch9BlDRZGxaA+bPws81Lufq4OHW9u2kfNv1r1W7TXYtMcgX61Iuv667eVJfzqr1P/9dzqry/qv/X/D4Lqb2TXf8NafrOp/yHs5h9O/Q9Hy37rDjfV/1Pwu8cf4QOiHc4w6nCqVWulzm7+4YjmHw0Il7WUPe0+QdV+2KkOIl7qsfqsWScYgLELBS9YPyEILILOhJsBwZ16vBnI+gCtz8k2N2KeYe4+bK/GkRFHtwN3sqC7HOhP+pNjt4f6Q2eIwkCAXtu9CtTXNICgAwQ5WhvB5lNsQBx0OXQYcfSlTTDaAHNQ+1jocTqaa5DBcIPR5qT0p+A7ERcQ7PFCKZTxQBe9kJdRj++l+vJeEFmgh7yk/ugKGFMXjKnfxD1HaiVvOHIEI9rh4q4VpdOCWt50WlyWmUYLEo5CiKAfPBL0AzBmTcJxWNfSCszEGwCawx7KTvvsc/UVKiDrAVmetPoWqE7OAW7i8GOlb8M9yxwwZ7Gi4MKLTtIG42zXw+WysPXX4kE4yuoyDxAgoBculwErNbv6sp1te9nOmTYHcOmy4aa11dcc1M//PDRfwAfIgC6P9Set68/ikB2rG1XOOwiyBu/SWo36Oj6PxwPB4fWPVH3pXPGYyub2nPOhAi7gst6u1eX11TZFc7XNeScBNnaurpT1gwLO5XJjfhb5MGuw0yB6aQYkTBC5qoJXL3GaLGi3KxBOb/csHUhkAsS0Wh1youoNZdARiMVmpXI89GA+dblYbq/WA0MNRo3NdrVcnO3+1vfgAXHg1uw6sCUaDY4LE3WvpLHmsIHLshm+vagMYAVssHMTvPYEyoJsx0g2n7AARMKVguAsVh/HBwEOYPr1Ypv1ne6uDKAAmPW4lKy/v/0Jr4tAJwOOESIj2cbYrCxeWH1zG1gEPhBw3qO+2AOmSUB+5DZxysqC92XgAgMumNloaT+ZD4YWJCa/zgkhR6xx7whUA2KVEy6O1Wshf0m81t/AnMKuJZNCz/XH7k0CqCKAKrg5JClyeYcX7arAACQ530qa6VRjMDsgYFT43Kbjz0OSGTsGgDrixKrv2gMPA0wHXOTWNxMAfoCT+Ry97cXDIBJA5ra5cW0/ogESBeTU5lyivfIKZH0g6HFj0n5sBBiEydfmwlW3gIMJrAVcRmrv9oEwQeLtEetZhAG/88aryJtbloAmQK3H9qF9YQz6PhwzdrCbSybAqIEZNmDFkKuH0B82XH/r+4SA24IE7nFu29wtCuY2SA+XDI9pjKdvuNNjbaVxlhkxIuDqpkngrNUD3hPbYGCqldHKslnr7fIXqYBTeV3Ccrg0ytStYP7cNEtD0awa7abm1hTbBLuGBVe7A4SADzYZwDesoLMBD+iRRDnSA/b8uvjmc06npWV9NzrADIbE5uYFIHvU169DBSBKbC5K+DktAAki4tisxE1HdmAq7JPM6y87A0cAU1nE5fvuZRGIFww5K4dKKyHAGXEufzZjh2D+izhe6jSZZImxfYGh6vM4m7fugVm4Ttg08SDaskpb4m1iht16Nh+jBZ4C15vNgrNRuGkyyoZz/+YuKOD/oJt+E7LsVh++Hw5nB7iqYQshhSzLJLvH5S60GuMEcRJzgDM4rLHH9NMpSXfG6g8C5QKtnka3zZtTUBxOg82xgTqg6NV0vsUHg+4OwDi2LxJCHHAN77Ns9S1EYcHAaw9KuOxxxnkIR2LTHFawyxOq8B9B7tkVY5ngdRgqKzSnMNxEUX/UD9iEA2ezvc2T+3tjYhIwqjnCy8KsikYow3JMmVIuGF23JidqOrtpwlR4zaTbZBKn+S9ukxPYeqJ6HRAmFhAZ7LicClk0Ly7D8IfLIbZa3wmTkQXHiZ2AzwrHITzt4/JHfQEdSHpwBmrGzuOcCHzbAXYaAna5OUnLbuuX6KE0nF3opfjHlXVMjjJVi43rm4/fvv0fgKDevv6SAAA="; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file diff --git a/packages/queries/src/query.test.ts b/packages/queries/src/query.test.ts index 882b9b98..c84f1662 100644 --- a/packages/queries/src/query.test.ts +++ b/packages/queries/src/query.test.ts @@ -1,5 +1,5 @@ import { describe, test } from 'vitest'; -import { Query, QueryResult, Table, col } from '.'; +import { Query, QueryResult, Table, col, param } from '.'; import { DB, from } from './generated'; describe('queries', () => { @@ -9,17 +9,6 @@ describe('queries', () => { return {} as any; } - test('Find one actor with `name()`', () => { - const q = from('actor') - .columns('actor_id', 'first_name') - .name('findActor') - .one(); - - const result = fakeQueryResult(q); - - result satisfies { actor_id: number; first_name: string }; - }); - test('Find one actor with `columns()`', () => { const q = from('actor').columns('actor_id', 'first_name').one(); @@ -54,6 +43,31 @@ describe('queries', () => { }; }); + test('Find one actor with `name()`', () => { + const q = from('actor') + .columns('actor_id', 'first_name') + .name('findActor') + .one(); + + const result = fakeQueryResult(q); + + result satisfies { actor_id: number; first_name: string }; + }); + + test('Find one actor with `param()`', () => { + const q = from('actor') + .columns('actor_id', 'first_name') + .where({ + actor_id: param('id', 1), + first_name: param('name', 'John'), + }) + .one(); + + const result = fakeQueryResult(q); + + result satisfies { actor_id: number; first_name: string }; + }); + test('Find many actors', () => { const q = from('actor').columns('actor_id', 'first_name').many(); diff --git a/packages/queries/src/types/AnyQuery.ts b/packages/queries/src/types/AnyQuery.ts index ae4de94d..14c97fd4 100644 --- a/packages/queries/src/types/AnyQuery.ts +++ b/packages/queries/src/types/AnyQuery.ts @@ -1,3 +1,4 @@ +import { Column } from './Column'; import { Table } from './Table'; import { Query } from './types'; @@ -16,4 +17,5 @@ export type AnyTableDef = { }; export type AnyDB = Record; export type AnyTable = Table; +export type AnyColumn = Column; export type AnyQuery = Query; diff --git a/packages/queries/src/types/BinaryOp.ts b/packages/queries/src/types/BinaryOp.ts index 0f0918f8..3c8c0b7c 100644 --- a/packages/queries/src/types/BinaryOp.ts +++ b/packages/queries/src/types/BinaryOp.ts @@ -1,6 +1,7 @@ import { Table } from './Table'; import { Column } from './Column'; import { ColumnValue } from './ColumnValue'; +import { QueryParameter } from './QueryParameter'; import { RefOp } from './RefOp'; export const BINARY_OPERATORS = [ @@ -68,7 +69,6 @@ export type BinaryOperator = (typeof BINARY_OPERATORS)[number]; * [operator]: value * } * ``` - * */ export type BinaryOp< DB, @@ -78,5 +78,6 @@ export type BinaryOp< [op in BinaryOperator | '= any']?: | ColumnValue | Array> + | QueryParameter> | RefOp; }; diff --git a/packages/queries/src/types/QueryParameter.ts b/packages/queries/src/types/QueryParameter.ts index 9ff3a203..444bc01f 100644 --- a/packages/queries/src/types/QueryParameter.ts +++ b/packages/queries/src/types/QueryParameter.ts @@ -1,5 +1,5 @@ export type QueryParameter = { type: 'synthql::parameter'; - id: string | number; + id: string; value: TValue; }; diff --git a/packages/queries/src/types/Where.ts b/packages/queries/src/types/Where.ts index d181f1b4..551484c2 100644 --- a/packages/queries/src/types/Where.ts +++ b/packages/queries/src/types/Where.ts @@ -9,10 +9,20 @@ import { WhereClause } from './WhereClause'; * * Currently, this can be either: * - * 1. `{column: {operator: value}}` example: `{age: {'>': 18}}`, which translates to `age > 18`. - * 1. `{column: value}` which is equivalent to `{column: {'=': value}}`. - * 1. `{column: col('table.column')}` which translates to `column = table.column`. + * 1. `{column: {operator: value}}`, example: `{age: {'>': 18}}`, + * which translates to ` WHERE age > 18`. * + * 1. `{column: value}`, example: `{age: 18}`, which is + * equivalent to `{age: {'=': 18}}` and translates to `WHERE age = 18`. + * + * 1. `{column: col('table.column')}`, example: `{age: col('users.bio')}`, + * which translates to `WHERE age = users.bio`. + * + * 1. `{column: param(value)}`, example: `{age: param(18)}`, + * which also translates to `WHERE age = 18`, but adds some metadata + * that 'marks' your query to be processed as a persisted query + * (after registering it via `QueryEngine.registerQueries()`), + * for even faster query execution. */ export type Where> = { [TColumn in Column]?: WhereClause; diff --git a/packages/queries/src/types/WhereClause.ts b/packages/queries/src/types/WhereClause.ts index e068f7b4..420686bf 100644 --- a/packages/queries/src/types/WhereClause.ts +++ b/packages/queries/src/types/WhereClause.ts @@ -1,14 +1,16 @@ import { Table } from './Table'; import { Column } from './Column'; +import { BinaryOp } from './BinaryOp'; import { ColumnValue } from './ColumnValue'; +import { QueryParameter } from './QueryParameter'; import { RefOp } from './RefOp'; -import { BinaryOp } from './BinaryOp'; export type WhereClause< DB, TTable extends Table, TColumn extends Column, > = - | ColumnValue | BinaryOp + | ColumnValue + | QueryParameter> | RefOp; From c96c81e0230624523793318eccbfe4f3ff8c73f6 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Tue, 10 Sep 2024 19:42:05 +0000 Subject: [PATCH 02/15] feat: added SqlBuilder handler logic for param() Signed-off-by: Jim Ezesinachi --- .../executors/PgExecutor/PgExecutor.test.ts | 44 ++++++++++-- .../executors/PgExecutor/composeQuery.ts | 2 +- .../executors/PgExecutor/queryBuilder/exp.ts | 67 ++++++++++++++++++- packages/queries/src/index.ts | 2 +- packages/queries/src/param.ts | 4 +- packages/queries/src/query.test.ts | 4 +- packages/queries/src/types/Where.ts | 2 +- packages/queries/src/util/hashQuery.ts | 2 +- .../{isQueryParam.ts => isQueryParameter.ts} | 0 9 files changed, 114 insertions(+), 13 deletions(-) rename packages/queries/src/validators/{isQueryParam.ts => isQueryParameter.ts} (100%) diff --git a/packages/backend/src/execution/executors/PgExecutor/PgExecutor.test.ts b/packages/backend/src/execution/executors/PgExecutor/PgExecutor.test.ts index 51af47ee..76ce0919 100644 --- a/packages/backend/src/execution/executors/PgExecutor/PgExecutor.test.ts +++ b/packages/backend/src/execution/executors/PgExecutor/PgExecutor.test.ts @@ -1,4 +1,4 @@ -import { col } from '@synthql/queries'; +import { col, param } from '@synthql/queries'; import { describe, expect, it } from 'vitest'; import { PgExecutor } from '.'; import { from } from '../../../tests/generated'; @@ -14,7 +14,7 @@ describe('PgExecutor', () => { prependSql: `SET search_path TO "public";`, }); - const q1 = from('film') + const q = from('film') .columns('film_id', 'title') .include({ lang: from('language') @@ -30,7 +30,7 @@ describe('PgExecutor', () => { .one(); it('Film table SynthQL query compiles to expected SQL query', () => { - const { sql } = executor.compile(q1); + const { sql } = executor.compile(q); expect(sql).toMatchInlineSnapshot(` "select @@ -50,7 +50,7 @@ describe('PgExecutor', () => { }); it('Film table SynthQL query executes to expected result', async () => { - const result = await executor.execute(q1, executeProps); + const result = await executor.execute(q, executeProps); expect(result).toEqual([ { @@ -63,6 +63,42 @@ describe('PgExecutor', () => { ]); }); + const q0 = from('actor') + .columns('actor_id', 'first_name') + .where({ + actor_id: param(2, 'id'), + }) + .one(); + + it('Actor table SynthQL query with `({ column: param(value) })` executes to expected result', async () => { + const result = await executor.execute(q0, executeProps); + + expect(result).toEqual([ + { + actor_id: 2, + first_name: 'NICK', + }, + ]); + }); + + const q1 = from('actor') + .columns('actor_id', 'first_name') + .where({ + actor_id: { '>': param(3, 'id') }, + }) + .one(); + + it('Actor table SynthQL query with param({ column: { op: param(value) } }) executes to expected result', async () => { + const result = await executor.execute(q1, executeProps); + + expect(result).toEqual([ + { + actor_id: 4, + first_name: 'JENNIFER', + }, + ]); + }); + const q2 = from('actor') .columns('actor_id', 'first_name', 'last_name') .take(2); diff --git a/packages/backend/src/execution/executors/PgExecutor/composeQuery.ts b/packages/backend/src/execution/executors/PgExecutor/composeQuery.ts index aef301a8..70284eb7 100644 --- a/packages/backend/src/execution/executors/PgExecutor/composeQuery.ts +++ b/packages/backend/src/execution/executors/PgExecutor/composeQuery.ts @@ -14,7 +14,7 @@ export function composeQuery({ }): { sqlBuilder: SqlBuilder; augmentedQuery: AugmentedQuery } { const augQuery: AugmentedQuery = createAugmentedQuery(query, defaultSchema); - let sqlBuilder = new SqlBuilder() + const sqlBuilder = new SqlBuilder() .select(augQuery.selection.map((s) => s.toSql())) .from(augQuery.rootTable) .leftJoins(augQuery.joins) diff --git a/packages/backend/src/execution/executors/PgExecutor/queryBuilder/exp.ts b/packages/backend/src/execution/executors/PgExecutor/queryBuilder/exp.ts index 2400d71c..3f300c44 100644 --- a/packages/backend/src/execution/executors/PgExecutor/queryBuilder/exp.ts +++ b/packages/backend/src/execution/executors/PgExecutor/queryBuilder/exp.ts @@ -1,4 +1,4 @@ -import { AnyDB } from '@synthql/queries'; +import { AnyDB, isQueryParameter } from '@synthql/queries'; import { OPERATORS, UnaryOperator } from 'kysely'; import { TableRef } from '../../../../refs/TableRef'; import { ColumnRef } from '../../../../refs/ColumnRef'; @@ -101,16 +101,20 @@ type Exp = export function compileExp(exp: Exp): SqlBuilder { const builder = new SqlBuilder(); + if (typeof exp === 'string') { return builder.addColumnReference(exp); } + switch (exp[0]) { case 'op': { const [_, op, ...exps] = exp; + if (exp[1] === '= any') { const [_, op, exp1, exp2] = exp; return compileExp(eqAny(exp1, exp2)); } + if (exp[1] === 'not = any') { const [_, op, exp1, exp2] = exp; return compileExp(not(eqAny(exp1, exp2))); @@ -176,6 +180,7 @@ export class SqlBuilder { // TODO: escape string return this.add(`'${value}'`).space(); } + if (Array.isArray(value)) { return this.openParen() .addInterleaved( @@ -184,6 +189,7 @@ export class SqlBuilder { ) .closeParen(); } + return this.add(String(value)).space(); } @@ -217,27 +223,33 @@ export class SqlBuilder { if (builders.length === 0) { return this; } + builders .flatMap((builder, i) => { if (i === 0) { return [builder]; } + return [separator.space(), builder]; }) .forEach((builder) => this.addBuilder(builder)); + return this; } addOperator(op: BinaryOperator) { const unknownOp = op as unknown; + if (!OPERATORS.includes(op as any)) { throw new Error(`Invalid operator: ${op}`); } + return this.add(op); } addFn(fn: Fn) { const [_, name, ...args] = fn; + return this.add(name) .openParen() .addInterleaved( @@ -249,12 +261,14 @@ export class SqlBuilder { addAs(as: As) { const [_, exp, alias] = as; + // TODO validate that alias is a valid alias return this.addBuilder(compileExp(exp)).space().add(`as "${alias}" `); } addOp(op: Op) { const [_, opName, ...exps] = op; + return this.openParen() .addInterleaved( exps.map((exp) => compileExp(exp)), @@ -294,15 +308,19 @@ export class SqlBuilder { */ space() { const lastPart = this.parts[this.parts.length - 1]; + if (!lastPart) { return this.add(' '); } + if (typeof lastPart !== 'string') { return this; } + if (lastPart.endsWith(' ')) { return this; } + return this.add(' '); } @@ -315,6 +333,7 @@ export class SqlBuilder { for (const builder of builders) { this.addBuilder(builder); } + return this; } @@ -348,6 +367,7 @@ export class SqlBuilder { if (limit === undefined) { return this; } + return this.add(`limit ${limit} `); } @@ -364,6 +384,7 @@ export class SqlBuilder { const own = cond.ownColumn.aliasQuoted(); const other = cond.otherColumn.aliasQuoted(); const op = cond.op; + return new SqlBuilder() .add(own) .space() @@ -382,6 +403,7 @@ export class SqlBuilder { for (const join of sorted) { this.leftJoin(join); } + return this; } @@ -389,11 +411,13 @@ export class SqlBuilder { if (offset === undefined) { return this; } + return this.add(`offset ${offset} `); } build() { const params: any[] = []; + return { sql: this.parts .map((part) => { @@ -419,19 +443,57 @@ export class SqlBuilder { const expressions = Object.entries(where) .map(([column, op]): Exp | undefined => { const quotedColumn = table.column(column).aliasQuoted(); + if (op === null) { return ['op', 'is', quotedColumn, ['const', null]]; } + if (isPrimitive(op)) { const exp: Exp = ['op', '=', quotedColumn, ['param', op]]; return exp; } + if (isRefOp(op)) { return undefined; } + + if (isQueryParameter(op)) { + assertPrimitive( + op.value, + `Expected value ${JSON.stringify(op.value)} to be a primitive in ${JSON.stringify(op)}`, + ); + + const exp: Exp = [ + 'op', + '=', + quotedColumn, + ['param', op.value], + ]; + + return exp; + } + if (typeof op === 'object' && Object.keys(op).length === 1) { const [opName, value] = Object.entries(op)[0]; + assertOp(opName); + + if (isQueryParameter(value)) { + assertPrimitive( + value.value, + `Expected value ${JSON.stringify(value.value)} to be a primitive in ${JSON.stringify(op)}`, + ); + + const exp: Exp = [ + 'op', + opName, + quotedColumn, + ['param', value.value], + ]; + + return exp; + } + assertPrimitive( value, `Expected value ${JSON.stringify(value)} to be a primitive in ${JSON.stringify(op)}`, @@ -449,6 +511,7 @@ export class SqlBuilder { if (expressions.length === 0) { return this; } + return this.addInterleaved(expressions, SqlBuilder.and()).space(); } @@ -458,9 +521,11 @@ export class SqlBuilder { const expressions = where .map((w) => new SqlBuilder().expressionFromWhere(w)) .filter((b) => !b.isEmpty()); + if (expressions.length === 0) { return this; } + return this.add('where ').addInterleaved(expressions, SqlBuilder.and()); } } diff --git a/packages/queries/src/index.ts b/packages/queries/src/index.ts index 2581be8d..2bd7fe80 100644 --- a/packages/queries/src/index.ts +++ b/packages/queries/src/index.ts @@ -14,7 +14,7 @@ export * from './types/Select'; export * from './types/Table'; export * from './types/Where'; export * from './types/WhereClause'; -export * from './validators/isQueryParam'; +export * from './validators/isQueryParameter'; export * from './validators/isRefOp'; export { col } from './col'; export { param } from './param'; diff --git a/packages/queries/src/param.ts b/packages/queries/src/param.ts index 5af649e4..11504fd6 100644 --- a/packages/queries/src/param.ts +++ b/packages/queries/src/param.ts @@ -1,12 +1,12 @@ import { QueryParameter } from './types/QueryParameter'; export function param( - id: string, value: TValue, + id: string, ): QueryParameter { return { type: 'synthql::parameter', - id, value, + id, }; } diff --git a/packages/queries/src/query.test.ts b/packages/queries/src/query.test.ts index c84f1662..ff5fcccb 100644 --- a/packages/queries/src/query.test.ts +++ b/packages/queries/src/query.test.ts @@ -58,8 +58,8 @@ describe('queries', () => { const q = from('actor') .columns('actor_id', 'first_name') .where({ - actor_id: param('id', 1), - first_name: param('name', 'John'), + actor_id: param(1, 'id'), + first_name: param('John', 'name'), }) .one(); diff --git a/packages/queries/src/types/Where.ts b/packages/queries/src/types/Where.ts index 551484c2..a38b9028 100644 --- a/packages/queries/src/types/Where.ts +++ b/packages/queries/src/types/Where.ts @@ -19,7 +19,7 @@ import { WhereClause } from './WhereClause'; * which translates to `WHERE age = users.bio`. * * 1. `{column: param(value)}`, example: `{age: param(18)}`, - * which also translates to `WHERE age = 18`, but adds some metadata + * which also translates to `WHERE age = ?`, and adds some metadata * that 'marks' your query to be processed as a persisted query * (after registering it via `QueryEngine.registerQueries()`), * for even faster query execution. diff --git a/packages/queries/src/util/hashQuery.ts b/packages/queries/src/util/hashQuery.ts index 44c1a1b2..de396e8c 100644 --- a/packages/queries/src/util/hashQuery.ts +++ b/packages/queries/src/util/hashQuery.ts @@ -1,5 +1,5 @@ import { AnyQuery } from '../types/AnyQuery'; -import { isQueryParameter } from '../validators/isQueryParam'; +import { isQueryParameter } from '../validators/isQueryParameter'; // Copied from: https://github.com/TanStack/query/blob/353e4ad7291645f27de6585e9897b45e46c666fb/packages/query-core/src/utils.ts#L205 /** diff --git a/packages/queries/src/validators/isQueryParam.ts b/packages/queries/src/validators/isQueryParameter.ts similarity index 100% rename from packages/queries/src/validators/isQueryParam.ts rename to packages/queries/src/validators/isQueryParameter.ts From 2ca9f2788cc9bd540888476e3b71cd0f58d15635 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Mon, 16 Sep 2024 11:17:04 +0000 Subject: [PATCH 03/15] wip Signed-off-by: Jim Ezesinachi --- packages/backend/src/QueryEngine.test.ts | 55 ++++++ packages/backend/src/QueryEngine.ts | 157 +++++++++++++++--- packages/backend/src/SynthqlError.ts | 96 ++++++----- .../executors/PgExecutor/PgExecutor.test.ts | 4 +- .../static/reference/assets/navigation.js | 2 +- .../docs/static/reference/assets/search.js | 2 +- packages/queries/src/index.ts | 4 + packages/queries/src/param.ts | 5 +- packages/queries/src/query.test.ts | 4 +- packages/queries/src/query.ts | 15 +- packages/queries/src/types/QueryParameter.ts | 2 +- packages/queries/src/types/QueryRequest.ts | 14 ++ .../src/util/iterateRecursively.test.ts | 26 +++ .../queries/src/util/iterateRecursively.ts | 55 ++++++ 14 files changed, 368 insertions(+), 73 deletions(-) create mode 100644 packages/backend/src/QueryEngine.test.ts create mode 100644 packages/queries/src/types/QueryRequest.ts create mode 100644 packages/queries/src/util/iterateRecursively.test.ts create mode 100644 packages/queries/src/util/iterateRecursively.ts diff --git a/packages/backend/src/QueryEngine.test.ts b/packages/backend/src/QueryEngine.test.ts new file mode 100644 index 00000000..5f268049 --- /dev/null +++ b/packages/backend/src/QueryEngine.test.ts @@ -0,0 +1,55 @@ +import { describe, expect, it } from 'vitest'; +import { queryEngine } from './tests/queryEngine'; +import { col, param } from '@synthql/queries'; +import { from } from './tests/generated'; +import { collectLast } from './util/generators/collectLast'; + +describe('QueryEngine', () => { + // TODO: complete and (possibly) move tests + it('1 + 1 equals 2', async () => { + const params = { + 'where.actor_id': 1, + 'where.film_id': 1, + 'include.film.where.language_id': 1, + 'include.film.include.language.where.last_update': + '2022-02-15 10:02:19+00', + }; + + function findFilmActor() { + return from('film_actor') + .where({ + actor_id: param(), + film_id: param(), + }) + .include({ + film: from('film') + .where({ + film_id: col('film_actor.film_id'), + language_id: param(), + }) + .include({ + language: from('language') + .where({ + language_id: col('film.language_id'), + last_update: param(), + }) + .maybe(), + }) + .maybe(), + }) + .maybe(); + } + + queryEngine.registerQueries([findFilmActor]); + + const q = findFilmActor(); + + const result = await collectLast( + queryEngine.executeRegisteredQuery(q.hash, params, { + returnLastOnly: true, + }), + ); + + expect(1 + 1).toEqual(2); + }); +}); diff --git a/packages/backend/src/QueryEngine.ts b/packages/backend/src/QueryEngine.ts index a09c496c..f1e41ebc 100644 --- a/packages/backend/src/QueryEngine.ts +++ b/packages/backend/src/QueryEngine.ts @@ -1,5 +1,12 @@ import { Pool } from 'pg'; -import { Query, QueryResult, Table } from '@synthql/queries'; +import { + AnyQuery, + isQueryParameter, + iterateRecursively, + Query, + QueryResult, + Table, +} from '@synthql/queries'; import { composeQuery } from './execution/executors/PgExecutor/composeQuery'; import { QueryPlan, collectLast } from '.'; import { QueryProvider } from './QueryProvider'; @@ -12,9 +19,11 @@ import { SynthqlError } from './SynthqlError'; export interface QueryEngineProps { /** - * The database connection string e.g. `postgresql://user:password@localhost:5432/db`. + * The database connection string. + * e.g. `postgresql://user:password@localhost:5432/db`. * - * If you use this option, SynthQL will create a conection pool for you internally. + * If you use this option, SynthQL will create + * a conection pool for you internally. */ url?: string; /** @@ -25,14 +34,20 @@ export interface QueryEngineProps { */ schema?: string; /** - * An optional SQL statement that will be sent before every SynthQL query. + * If true, the executor will execute queries that have not + * been registered via `QueryEngine.registerQueries()`. + */ + dangerouslyAllowUnregisteredQueries?: boolean; + /** + * An optional SQL statement that will + * be sent before every SynthQL query. * * e.g `SELECT version();` */ prependSql?: string; /** - * A list of providers that you want to be used - * to execute your SynthQL queries against. + * A list of providers that you want to + * execute your SynthQL queries against. * * e.g: * @@ -55,9 +70,11 @@ export interface QueryEngineProps { */ providers?: Array>>; /** - * The connection pool to which the executor will send SQL queries to. + * The connection pool to which the + * executor will send SQL queries to. * - * You can use this instead of passing a connection string. + * You can use this instead of + * passing a connection string. */ pool?: Pool; @@ -70,18 +87,24 @@ export interface QueryEngineProps { export class QueryEngine { private pool: Pool; private schema: string; + private dangerouslyAllowUnregisteredQueries: boolean; private prependSql?: string; + // TODO: fix the callback return type from AnyQuery to TQuery + private queries: Map AnyQuery>; private executors: Array = []; constructor(config: QueryEngineProps) { - this.schema = config.schema ?? 'public'; - this.prependSql = config.prependSql; this.pool = config.pool ?? new Pool({ connectionString: config.url, max: 10, }); + this.schema = config.schema ?? 'public'; + this.dangerouslyAllowUnregisteredQueries = + config.dangerouslyAllowUnregisteredQueries ?? false; + this.prependSql = config.prependSql; + this.queries = new Map(); const qpe = new QueryProviderExecutor(config.providers ?? []); this.executors = [ @@ -96,12 +119,24 @@ export class QueryEngine { ]; } + compile(query: T extends Query ? T : never): { + sql: string; + params: any[]; + } { + const { sqlBuilder } = composeQuery({ + defaultSchema: this.schema, + query, + }); + + return sqlBuilder.build(); + } + execute, TQuery extends Query>( query: TQuery, opts?: { /** - * The name of the database schema to execute - * your SynthQL query against + * The name of the database schema to + * execute your SynthQL query against * * e.g `public` */ @@ -113,6 +148,15 @@ export class QueryEngine { returnLastOnly?: boolean; }, ): AsyncGenerator> { + if (!this.dangerouslyAllowUnregisteredQueries) { + const queryFn = this.queries.get(query.hash ?? ''); + + // TODO: add appropriate error method + if (!queryFn) { + throw new Error('Query has not been registered!'); + } + } + const gen = execute(query, { executors: this.executors, defaultSchema: opts?.schema ?? this.schema, @@ -133,14 +177,23 @@ export class QueryEngine { query: TQuery, opts?: { /** - * The name of the database schema to execute - * your SynthQL query against + * The name of the database schema to + * execute your SynthQL query against * * e.g `public` */ schema?: string; }, ): Promise> { + if (!this.dangerouslyAllowUnregisteredQueries) { + const queryFn = this.queries.get(query.hash ?? ''); + + // TODO: add appropriate error method + if (!queryFn) { + throw new Error('Query has not been registered!'); + } + } + return await collectLast( generateLast( execute(query, { @@ -152,18 +205,63 @@ export class QueryEngine { ); } - compile(query: T extends Query ? T : never): { - sql: string; - params: any[]; - } { - const { sqlBuilder } = composeQuery({ - defaultSchema: this.schema, - query, + // TODO: fix generic types for input and return types + // Currently returning `AsyncGenerator` + executeRegisteredQuery< + TTable extends Table, + TQuery extends Query, + >( + queryId: string, + params: Record, + opts?: { + /** + * The name of the database schema to + * execute your SynthQL query against + * + * e.g `public` + */ + schema?: string; + /** + * If true, the query result generator will wait for query + * execution completion, and then return only the last result + */ + returnLastOnly?: boolean; + }, + ): AsyncGenerator> { + const queryFn = this.queries.get(queryId); + + // TODO: add appropriate error method + if (!queryFn) { + throw new Error('Query has not been registered!'); + } + + const query = queryFn(); + + // TODO: possibly wrap this logic in a wrapper function + // with a better descriptive name, and documentation + iterateRecursively(query, (x, path) => { + if (isQueryParameter(x)) { + // TODO: possibly throw error if params?.[x.id]; is undefined? + x.value = params?.[x.id]; + } }); - return sqlBuilder.build(); + // TODO: Remove this 'as any', after fixing types + const gen = execute(query as any, { + executors: this.executors, + defaultSchema: opts?.schema ?? this.schema, + prependSql: this.prependSql, + }); + + if (opts?.returnLastOnly) { + return generateLast(gen); + } + + return gen; } + // TODO: possibly add a `executeRegisteredQueryAndWait()` + async explain>( query: Query, ): Promise { @@ -186,4 +284,19 @@ export class QueryEngine { }); } } + + // TODO: fix the callback return type from AnyQuery to TQuery + // Not sure how to do this yet, or if this is even possible + registerQueries(queryFns: Array<(...params: any[]) => AnyQuery>) { + for (const queryFn of queryFns) { + const query = queryFn(); + + // TODO: add appropriate error method + if (!query.hash) { + throw new Error('Query to be registered is missing a hash!'); + } + + this.queries.set(query.hash, queryFn); + } + } } diff --git a/packages/backend/src/SynthqlError.ts b/packages/backend/src/SynthqlError.ts index a91b6741..ee92390f 100644 --- a/packages/backend/src/SynthqlError.ts +++ b/packages/backend/src/SynthqlError.ts @@ -19,6 +19,25 @@ export class SynthqlError extends Error { Error.captureStackTrace(this, SynthqlError); } + // TODO: add appropriate static methods for the new errors in the + // QueryEngine: registerQueries, executeParameterizedQuery, and + // possibly, executeParameterizedQueryAndWait + + static createCardinalityError() { + const type = 'CardinalityError'; + + // Two ways this error can happen: + // 1. The top level query returned no results. + // 2. A subquery returned no results. + + const lines = [ + 'A query with a cardinality of `one` returned no results!', + 'Hint: are you using .one() when you should be using .maybe()?', + ]; + + return new SynthqlError(new Error(), type, lines.join('\n'), 404); + } + static createDatabaseConnectionError({ error, }: { @@ -31,8 +50,8 @@ export class SynthqlError extends Error { '', 'Failure to establish a connection to your database.', '', - 'Check your connection string, and make sure your database', - 'is up and can accept new connections.', + 'Check your connection string, and make sure your', + 'database is up and can accept new connections.', '', 'Here is the underlying error message:', '', @@ -41,18 +60,36 @@ export class SynthqlError extends Error { return new SynthqlError(error, type, lines.join('\n')); } - static createSqlExecutionError({ + + static createJsonParsingError({ error, - props, + json, }: { error: any; - props: SqlExecutionErrorProps; + json: string; }): SynthqlError { - const type = 'SqlExecutionError'; + const type = 'JsonParsingError'; - const message = composeMessage(error, props); + const lines = [ + 'JSON parsing error!', + '', + 'Expected a JSON string but got this instead:', + '', + json, + '', + 'Check your query and make sure your stringifier', + 'function/method is behaving as expected', + ]; - return new SynthqlError(error, type, message); + return new SynthqlError(error, type, lines.join('\n')); + } + + static createMissingHashError() { + const type = 'MissingHashError'; + + const lines = ['A query passed to be registered, is missing its hash!']; + + return new SynthqlError(new Error(), type, lines.join('\n')); } static createPrependSqlExecutionError({ @@ -92,48 +129,27 @@ export class SynthqlError extends Error { '', JSON.stringify(query, null, 2), '', - 'Check your query and make sure you have `read` access to all included', - 'tables and columns, and have registered all queries via the QueryEngine', + 'Check your query and make sure you', + 'have `read` access to all included', + 'tables and columns, and registered', + 'all queries via the QueryEngine', ]; return new SynthqlError(error, type, lines.join('\n')); } - static createJsonParsingError({ + static createSqlExecutionError({ error, - json, + props, }: { error: any; - json: string; + props: SqlExecutionErrorProps; }): SynthqlError { - const type = 'JsonParsingError'; - - const lines = [ - 'JSON parsing error!', - '', - 'Expected a JSON string but got this instead:', - '', - json, - '', - 'Check your query and make sure your stringifying function is behaving as expected', - ]; - - return new SynthqlError(error, type, lines.join('\n')); - } - - static createCardinalityError() { - const type = 'CardinalityError'; - - // Two ways this error can happen: - // 1. The top level query returned no results. - // 2. A subquery returned no results. + const type = 'SqlExecutionError'; - const lines = [ - 'A query with a cardinality of `one` returned no results!', - 'Hint: are you using .one() when you should be using .maybe()?', - ]; + const message = composeMessage(error, props); - return new SynthqlError(new Error(), type, lines.join('\n'), 404); + return new SynthqlError(error, type, message); } } @@ -149,7 +165,7 @@ function printError(err: any): string { function composeMessage(err: any, props: SqlExecutionErrorProps): string { const lines: string[] = [ - '# Error executing query', + '# Error executing query:', '', printError(err), '', diff --git a/packages/backend/src/execution/executors/PgExecutor/PgExecutor.test.ts b/packages/backend/src/execution/executors/PgExecutor/PgExecutor.test.ts index 76ce0919..fbe812fb 100644 --- a/packages/backend/src/execution/executors/PgExecutor/PgExecutor.test.ts +++ b/packages/backend/src/execution/executors/PgExecutor/PgExecutor.test.ts @@ -66,7 +66,7 @@ describe('PgExecutor', () => { const q0 = from('actor') .columns('actor_id', 'first_name') .where({ - actor_id: param(2, 'id'), + actor_id: param(2), }) .one(); @@ -84,7 +84,7 @@ describe('PgExecutor', () => { const q1 = from('actor') .columns('actor_id', 'first_name') .where({ - actor_id: { '>': param(3, 'id') }, + actor_id: { '>': param(3) }, }) .one(); diff --git a/packages/docs/static/reference/assets/navigation.js b/packages/docs/static/reference/assets/navigation.js index a9e347be..17b69f28 100644 --- a/packages/docs/static/reference/assets/navigation.js +++ b/packages/docs/static/reference/assets/navigation.js @@ -1 +1 @@ -window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE5WXbW/bIBCA/4s/p+vard2Wb2kaad2mNkuiTVNVVRRfalQCDuAq1rT/PvklfgMf9Gt4eIDjOF/u/0YGDiaaRk+EvoCIT7Wi0SRKiUmiabSTccZBn9aDj1rRd4nZ8WgSvTARR9PzSUQTxmMFIpreN7KfGah8IZ6ZgFZGOdF6IOuAffHZ+ed/k8a3zoVJ9nyhlFS4sEtixs7CSyVT3VqZMKC2hI7vtJwwiMPF5VC+5ESEWQsS01HJOVDzg2jTCreZoIZJ0fd10L7x8mNPuEulhnJxv7FlLeVDV8qZO3koZ47E6cxMiIg5qBM4pAq0dltq6LGGglORKiAGFtWsOju+Vi7X0V3LIAo0IsdzCTgY/FAFEXyiWziYsYOYPHVp7SmDld5/+XR20b0Te8YK9hl0MzB4rXqmb8kqzNjp7GtqVh2bjF4QE0ZJnQIduZ52PPhynkGAIgZcmx7ojii6xX0GisHIm6gHgzc3lzzbiWvYOutS19aQWF1qoKIggjIM3GXUKW7nBC3xBjMqXNMEdsQrqzBMtCFPHEJCeQQx2Uzk1faHr6sraiDfQ5qJ/PrKY7q+CrAMPhBOkePD4HSVYfC4SibU1Yv9uM6OvGW8YoKo/C7FdEcm1FW8bGkVZpexIn3eOVExE4Qzg15IB/MavQkXlm0VtclT9HZbKsz3i/AsQFhiPuONoDyLUVuN+EzfJBN4nlSEz+N9WEGvqmoeiSI7MHYTYOkaMsi7Ap1x62tvSSvMZ1zBFg9bCfgsayh6W0xTET6PtxQF1aHfCSjUUgJBljknmfa7KsxbgW5uZ6s/j3fLxWq2uVutW+0rUaw42aAODfi+/0OvQ5Pc1dd0bVRy5I8H02M567YNeVQ9SLIxoyPVeqK0WMynKSFEsh/7a9WVlNDJGdr/KSBj3Wk5FNz71W3xXIryB1fX0gr7MNpPVWR5Td/BqmmW8gj6sliPbLfN4VbdZ4cB7WVwvYulkq8sduefteUjjCbfaBxcYgtH1Pot4gHsyK1WnGmod4E7Wy5IZt0Y7nSmWPkGHv4DDtAzbZwSAAA=" \ No newline at end of file +window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE5WXbU/bMBCA/0s+lzHYYFu/lVIJtgm6FG2aEELGuRIP10ltB7Wa9t+nvDWJ7ZzN1+a5xy93vtr3fyMNOx1NoydCX0Akx0rSaBLlRKfRNNpkScFBHTcfH5Wk71K94dEkemEiiaank4imjCcSRDS9P8h+FCD3C/HMBHQyyolShqwHDsUnp5//TQ6+1V7odMsXUmYSF/ZJzNgbeCmzXHVWJjTINaHjM60CjH04OzflS05EmLUkMR3NOAeqvxOlO+G6EFSzTAx9PXRoPP84EG7yTEE1uN/YsZbyoS/lzF08lDNH4fQiUyISDvIIdrkEpdyWBnpsoOBSpBKIhkUd1VTHVe1yLd01DKJAd6Rdl4CdxhdVEsEruoGdHluI3ucurR1ijPT+y6eTs35O7IgYtgX0KzB4rCbSN2S9zdjq7DQdRh0LRhPEhJaZyoGOpKf7HpycZxAgiQbXpA1di6JT3BYgGYycieZj8OTmGS824hLWzr7Utx1IrC8doLIhgtQM3G3UKe5igoZ4gxkVxvDMlAYJSdXRrKIekbvDPAMVnMi3jmLGYEOsaAob4rXWGCa6I08cQsqiBTHZTOzrVJidoi86QL6mMBP7ywuP6fIiwGL82TlFjj85p6vaBo+rYkJdg70f19k7bxkvmCByf5tjupYJdZVdKrP+ZFzGmvR550QmTBDONJqQHuY1egsurNpqKoY1SBAUTbGBhpnv9nmAtKTCfD8JLwKEFeYzXgvKiwS1NYjP9DVjAq/AmvB5vEc26LzWV2wiyQa0fVWydAcyyDtyKbKsgVeghlUFD1GWmM8YwxpPRQX4LCsoXxWYpiZ8Hm/jDOqav1KQqKUCgixzTgrld9WYt19e38zi34+3y0U8u7uNV532lUhWrszomgY/9H8Y3I0z7rpR9m0048iTL/nzdHpFVOqztByiSolKR5+OgytpCyIypsbOp9tp8qjaKP4xo+MIDEW6uqfHQAup2Ctw78rtCESfl2vxGSsIkWxDElJBRyfog0MCGXsOVZ+CHxvNO2yeieoH19WyEw5h9NJbk1UVfAPr78FStqDv8KqR6XZHt1MPWXNDBwe3mcVSZq8scZe3NeUWRmt7dB9cYgtH1OotYgN21FYnLhQ0s8CdHRckszKGO50lVp2Bh/9Ohll4DRUAAA==" \ No newline at end of file diff --git a/packages/docs/static/reference/assets/search.js b/packages/docs/static/reference/assets/search.js index bf49072c..0c675d3e 100644 --- a/packages/docs/static/reference/assets/search.js +++ b/packages/docs/static/reference/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file diff --git a/packages/queries/src/index.ts b/packages/queries/src/index.ts index 2bd7fe80..9a67a7f1 100644 --- a/packages/queries/src/index.ts +++ b/packages/queries/src/index.ts @@ -3,10 +3,12 @@ export * from './types/AnyQuery'; export * from './types/BinaryOp'; export * from './types/Cardinality'; export * from './types/Column'; +export * from './types/ColumnReference'; export * from './types/ColumnValue'; export * from './types/Include'; export * from './types/JoinOp'; export * from './types/QueryParameter'; +export * from './types/QueryRequest'; export * from './types/QueryResult'; export * from './types/RefOp'; export * from './types/Schema'; @@ -16,6 +18,8 @@ export * from './types/Where'; export * from './types/WhereClause'; export * from './validators/isQueryParameter'; export * from './validators/isRefOp'; +export * from './util/hashQuery'; +export * from './util/iterateRecursively'; export { col } from './col'; export { param } from './param'; export { query } from './query'; diff --git a/packages/queries/src/param.ts b/packages/queries/src/param.ts index 11504fd6..facba005 100644 --- a/packages/queries/src/param.ts +++ b/packages/queries/src/param.ts @@ -1,12 +1,11 @@ import { QueryParameter } from './types/QueryParameter'; export function param( - value: TValue, - id: string, + value?: TValue, ): QueryParameter { return { type: 'synthql::parameter', value, - id, + id: String(value), }; } diff --git a/packages/queries/src/query.test.ts b/packages/queries/src/query.test.ts index ff5fcccb..89845def 100644 --- a/packages/queries/src/query.test.ts +++ b/packages/queries/src/query.test.ts @@ -58,8 +58,8 @@ describe('queries', () => { const q = from('actor') .columns('actor_id', 'first_name') .where({ - actor_id: param(1, 'id'), - first_name: param('John', 'name'), + actor_id: param(), + first_name: param(), }) .one(); diff --git a/packages/queries/src/query.ts b/packages/queries/src/query.ts index c561cf58..c98b7fdd 100644 --- a/packages/queries/src/query.ts +++ b/packages/queries/src/query.ts @@ -9,6 +9,8 @@ import { getTableSelectableColumns } from './schema/getTableSelectableColumns'; import { getTablePrimaryKeyColumns } from './schema/getTablePrimaryKeyColumns'; import { validateNestedQueriesHaveAValidRefOp } from './validators/validateNestedQueriesHaveAValidRefOp'; import { hashQuery } from './util/hashQuery'; +import { iterateRecursively } from './util/iterateRecursively'; +import { isQueryParameter } from './validators/isQueryParameter'; export class QueryBuilder< DB, @@ -61,7 +63,7 @@ export class QueryBuilder< hash: string; name?: string; } { - return { + const query = { from: this._from, where: this._where, select: this._select, @@ -85,6 +87,17 @@ export class QueryBuilder< }), name: this._name, }; + + // TODO: possibly wrap this logic in a wrapper function + // with a better descriptive name, and documentation + // Assigning identifiers for parameterized queries + iterateRecursively(query, (x, path) => { + if (isQueryParameter(x)) { + x.id = path.join('.'); + } + }); + + return query; } /** diff --git a/packages/queries/src/types/QueryParameter.ts b/packages/queries/src/types/QueryParameter.ts index 444bc01f..e13ef8c1 100644 --- a/packages/queries/src/types/QueryParameter.ts +++ b/packages/queries/src/types/QueryParameter.ts @@ -1,5 +1,5 @@ export type QueryParameter = { type: 'synthql::parameter'; id: string; - value: TValue; + value: TValue | undefined; }; diff --git a/packages/queries/src/types/QueryRequest.ts b/packages/queries/src/types/QueryRequest.ts new file mode 100644 index 00000000..0c64ad06 --- /dev/null +++ b/packages/queries/src/types/QueryRequest.ts @@ -0,0 +1,14 @@ +import { AnyQuery } from './AnyQuery'; + +export interface RegularQueryRequest { + type: 'RegularQuery'; + query: AnyQuery; +} + +export interface RegisteredQueryRequest { + type: 'RegisteredQuery'; + queryId: string; + params: Record; +} + +export type QueryRequest = RegularQueryRequest | RegisteredQueryRequest; diff --git a/packages/queries/src/util/iterateRecursively.test.ts b/packages/queries/src/util/iterateRecursively.test.ts new file mode 100644 index 00000000..21e0abf4 --- /dev/null +++ b/packages/queries/src/util/iterateRecursively.test.ts @@ -0,0 +1,26 @@ +import { describe, test } from 'vitest'; +import { col } from '../col'; +import { param } from '../param'; +import { from } from '../generated'; + +describe('iterateRecursively', () => { + // TODO: complete test or remove it + test('Find one film_actor with `param()`', () => { + const q = from('film_actor') + .columns('actor_id', 'film_id', 'last_update') + .where({ + actor_id: param(1), + film_id: param(1), + }) + .include({ + film: from('film') + .columns('film_id', 'title') + .where({ + film_id: col('film_actor.film_id'), + language_id: param(1), + }) + .maybe(), + }) + .maybe(); + }); +}); diff --git a/packages/queries/src/util/iterateRecursively.ts b/packages/queries/src/util/iterateRecursively.ts new file mode 100644 index 00000000..beec8914 --- /dev/null +++ b/packages/queries/src/util/iterateRecursively.ts @@ -0,0 +1,55 @@ +// TODO: Unite/move this type with the one of the same +// name already being used in `backend` and `queries` +// See: +// packages/backend/src/execution/executors/PgExecutor/queryBuilder/exp.ts +// packages/queries/src/expression/Expression.ts +type Primitive = + | string + | number + | boolean + | null + | undefined + | symbol + | bigint + | Date; + +type Traversable = + | Primitive + | { [key: string | number]: Traversable } + | Array; + +// TODO: possibly rename to better, more descriptive, OR +// create specific named wrappers that use it internally +export function iterateRecursively( + traversable: T, + visitor: (traversable: Traversable, path: string[]) => void, + path: string[] = [], +): void { + // Apply the visitor to the current traversable + visitor(traversable, path); + + if ( + typeof traversable === 'object' && + traversable !== null && + !(traversable instanceof Date) + ) { + if (Array.isArray(traversable)) { + // If it's an array, iterate over each element + // with its index as the key in the path + traversable.forEach((item, index) => { + iterateRecursively(item, visitor, [...path, String(index)]); + }); + } else { + // If it's an object, iterate over each + // property with its key in the path + for (const key in traversable) { + if (Object.prototype.hasOwnProperty.call(traversable, key)) { + iterateRecursively(traversable[key], visitor, [ + ...path, + String(key), + ]); + } + } + } + } +} From e961dccbf5f0bedb787107f60a15746fe9539f8c Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Mon, 16 Sep 2024 19:49:07 +0000 Subject: [PATCH 04/15] fixed tests Signed-off-by: Jim Ezesinachi --- packages/backend/src/QueryEngine.test.ts | 88 +++++++++++-------- packages/backend/src/QueryEngine.ts | 49 +++++++---- packages/backend/src/tests/queryEngine.ts | 1 + .../docs/static/reference/assets/search.js | 2 +- .../handler-next/src/tests/queryEngine.ts | 1 + packages/queries/src/query.ts | 2 +- packages/react/src/useSynthql.test.tsx | 1 + 7 files changed, 86 insertions(+), 58 deletions(-) diff --git a/packages/backend/src/QueryEngine.test.ts b/packages/backend/src/QueryEngine.test.ts index 5f268049..d5a91010 100644 --- a/packages/backend/src/QueryEngine.test.ts +++ b/packages/backend/src/QueryEngine.test.ts @@ -1,55 +1,67 @@ import { describe, expect, it } from 'vitest'; -import { queryEngine } from './tests/queryEngine'; import { col, param } from '@synthql/queries'; +import { queryEngine } from './tests/queryEngine'; import { from } from './tests/generated'; -import { collectLast } from './util/generators/collectLast'; describe('QueryEngine', () => { - // TODO: complete and (possibly) move tests - it('1 + 1 equals 2', async () => { + it('registerQueries + executeRegisteredQuery', async () => { + queryEngine.registerQueries([findFilmActor]); + const params = { - 'where.actor_id': 1, - 'where.film_id': 1, - 'include.film.where.language_id': 1, + 'where.actor_id': 2, + 'where.film_id': 47, + 'include.film.where.language_id': 3, 'include.film.include.language.where.last_update': '2022-02-15 10:02:19+00', }; - function findFilmActor() { - return from('film_actor') + const parameterizedQueryResult = + await queryEngine.executeRegisteredQueryAndWait( + findFilmActor().hash, + params, + ); + + const regularQuery = findFilmActor({ + actor_id: params['where.actor_id'], + film_id: params['where.film_id'], + language_id: params['include.film.where.language_id'], + last_update: + params['include.film.include.language.where.last_update'], + }); + + const regularQueryResult = + await queryEngine.executeAndWait(regularQuery); + + expect(parameterizedQueryResult).toEqual(regularQueryResult); + }); +}); + +function findFilmActor(data?: { + actor_id?: number; + film_id?: number; + language_id?: number; + last_update?: string; +}) { + return from('film_actor') + .where({ + actor_id: data?.actor_id ?? param(), + film_id: data?.film_id ?? param(), + }) + .include({ + film: from('film') .where({ - actor_id: param(), - film_id: param(), + film_id: col('film_actor.film_id'), + language_id: data?.language_id ?? param(), }) .include({ - film: from('film') + language: from('language') .where({ - film_id: col('film_actor.film_id'), - language_id: param(), - }) - .include({ - language: from('language') - .where({ - language_id: col('film.language_id'), - last_update: param(), - }) - .maybe(), + language_id: col('film.language_id'), + last_update: data?.last_update ?? param(), }) .maybe(), }) - .maybe(); - } - - queryEngine.registerQueries([findFilmActor]); - - const q = findFilmActor(); - - const result = await collectLast( - queryEngine.executeRegisteredQuery(q.hash, params, { - returnLastOnly: true, - }), - ); - - expect(1 + 1).toEqual(2); - }); -}); + .maybe(), + }) + .maybe(); +} diff --git a/packages/backend/src/QueryEngine.ts b/packages/backend/src/QueryEngine.ts index f1e41ebc..b32b1f9e 100644 --- a/packages/backend/src/QueryEngine.ts +++ b/packages/backend/src/QueryEngine.ts @@ -185,23 +185,11 @@ export class QueryEngine { schema?: string; }, ): Promise> { - if (!this.dangerouslyAllowUnregisteredQueries) { - const queryFn = this.queries.get(query.hash ?? ''); - - // TODO: add appropriate error method - if (!queryFn) { - throw new Error('Query has not been registered!'); - } - } - - return await collectLast( - generateLast( - execute(query, { - executors: this.executors, - defaultSchema: opts?.schema ?? this.schema, - prependSql: this.prependSql, - }), - ), + return collectLast( + this.execute(query, { + schema: opts?.schema ?? this.schema, + returnLastOnly: true, + }), ); } @@ -260,7 +248,31 @@ export class QueryEngine { return gen; } - // TODO: possibly add a `executeRegisteredQueryAndWait()` + // TODO: fix generic types for input and return types + // Currently returning `AsyncGenerator` + executeRegisteredQueryAndWait< + TTable extends Table, + TQuery extends Query, + >( + queryId: string, + params: Record, + opts?: { + /** + * The name of the database schema to + * execute your SynthQL query against + * + * e.g `public` + */ + schema?: string; + }, + ): Promise> { + return collectLast( + this.executeRegisteredQuery(queryId, params, { + schema: opts?.schema ?? this.schema, + returnLastOnly: true, + }), + ); + } async explain>( query: Query, @@ -276,6 +288,7 @@ export class QueryEngine { try { const result = await this.pool.query(explainQuery, params); + return result.rows[0]['QUERY PLAN'][0]; } catch (err) { throw SynthqlError.createSqlExecutionError({ diff --git a/packages/backend/src/tests/queryEngine.ts b/packages/backend/src/tests/queryEngine.ts index 81019625..887dc88d 100644 --- a/packages/backend/src/tests/queryEngine.ts +++ b/packages/backend/src/tests/queryEngine.ts @@ -13,4 +13,5 @@ export const pool = new Pool({ export const queryEngine = new QueryEngine({ pool, schema: 'public', + dangerouslyAllowUnregisteredQueries: true, }); diff --git a/packages/docs/static/reference/assets/search.js b/packages/docs/static/reference/assets/search.js index 0c675d3e..87d1e2c8 100644 --- a/packages/docs/static/reference/assets/search.js +++ b/packages/docs/static/reference/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file diff --git a/packages/handler-next/src/tests/queryEngine.ts b/packages/handler-next/src/tests/queryEngine.ts index 71b38dc4..50dd17b3 100644 --- a/packages/handler-next/src/tests/queryEngine.ts +++ b/packages/handler-next/src/tests/queryEngine.ts @@ -9,4 +9,5 @@ export const pool = new Pool({ export const queryEngine = new QueryEngine({ pool, schema: 'public', + dangerouslyAllowUnregisteredQueries: true, }); diff --git a/packages/queries/src/query.ts b/packages/queries/src/query.ts index c98b7fdd..eeaf9b8c 100644 --- a/packages/queries/src/query.ts +++ b/packages/queries/src/query.ts @@ -89,7 +89,7 @@ export class QueryBuilder< }; // TODO: possibly wrap this logic in a wrapper function - // with a better descriptive name, and documentation + // with a better descriptive name, and documentation // Assigning identifiers for parameterized queries iterateRecursively(query, (x, path) => { if (isQueryParameter(x)) { diff --git a/packages/react/src/useSynthql.test.tsx b/packages/react/src/useSynthql.test.tsx index b60b7c61..9b83de39 100644 --- a/packages/react/src/useSynthql.test.tsx +++ b/packages/react/src/useSynthql.test.tsx @@ -45,6 +45,7 @@ describe('useSynthql', () => { beforeAll(async () => { const queryEngine = new QueryEngine({ url: 'postgres://postgres:postgres@localhost:5432/postgres', + dangerouslyAllowUnregisteredQueries: true, }); pagilaServer = await createPagilaServer({ queryEngine }); From 35607cefc89c338f43b3156047bd5b6210a620d0 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Tue, 17 Sep 2024 19:18:26 +0000 Subject: [PATCH 05/15] added error methods and fixes Signed-off-by: Jim Ezesinachi --- packages/backend/src/QueryEngine.test.ts | 6 +-- packages/backend/src/QueryEngine.ts | 54 +++++++++++++------ packages/backend/src/SynthqlError.ts | 52 +++++++++++++++--- .../docs/static/reference/assets/search.js | 2 +- packages/queries/src/query.ts | 3 -- packages/queries/src/types/QueryRequest.ts | 4 +- .../src/util/iterateRecursively.test.ts | 26 --------- .../queries/src/util/iterateRecursively.ts | 2 - 8 files changed, 89 insertions(+), 60 deletions(-) delete mode 100644 packages/queries/src/util/iterateRecursively.test.ts diff --git a/packages/backend/src/QueryEngine.test.ts b/packages/backend/src/QueryEngine.test.ts index d5a91010..251fe8a5 100644 --- a/packages/backend/src/QueryEngine.test.ts +++ b/packages/backend/src/QueryEngine.test.ts @@ -16,10 +16,10 @@ describe('QueryEngine', () => { }; const parameterizedQueryResult = - await queryEngine.executeRegisteredQueryAndWait( - findFilmActor().hash, + await queryEngine.executeRegisteredQueryAndWait({ + queryId: findFilmActor().hash, params, - ); + }); const regularQuery = findFilmActor({ actor_id: params['where.actor_id'], diff --git a/packages/backend/src/QueryEngine.ts b/packages/backend/src/QueryEngine.ts index b32b1f9e..babfa0ec 100644 --- a/packages/backend/src/QueryEngine.ts +++ b/packages/backend/src/QueryEngine.ts @@ -149,11 +149,18 @@ export class QueryEngine { }, ): AsyncGenerator> { if (!this.dangerouslyAllowUnregisteredQueries) { - const queryFn = this.queries.get(query.hash ?? ''); + if (!query.hash) { + throw SynthqlError.createMissingHashError({ + query, + }); + } + + const queryFn = this.queries.get(query.hash); - // TODO: add appropriate error method if (!queryFn) { - throw new Error('Query has not been registered!'); + throw SynthqlError.createQueryNotRegisteredError({ + queryId: query.hash, + }); } } @@ -199,8 +206,10 @@ export class QueryEngine { TTable extends Table, TQuery extends Query, >( - queryId: string, - params: Record, + { + queryId, + params, + }: { queryId: string; params: Record }, opts?: { /** * The name of the database schema to @@ -218,15 +227,14 @@ export class QueryEngine { ): AsyncGenerator> { const queryFn = this.queries.get(queryId); - // TODO: add appropriate error method if (!queryFn) { - throw new Error('Query has not been registered!'); + throw SynthqlError.createQueryNotRegisteredError({ + queryId, + }); } const query = queryFn(); - // TODO: possibly wrap this logic in a wrapper function - // with a better descriptive name, and documentation iterateRecursively(query, (x, path) => { if (isQueryParameter(x)) { // TODO: possibly throw error if params?.[x.id]; is undefined? @@ -254,8 +262,10 @@ export class QueryEngine { TTable extends Table, TQuery extends Query, >( - queryId: string, - params: Record, + { + queryId, + params, + }: { queryId: string; params: Record }, opts?: { /** * The name of the database schema to @@ -267,10 +277,13 @@ export class QueryEngine { }, ): Promise> { return collectLast( - this.executeRegisteredQuery(queryId, params, { - schema: opts?.schema ?? this.schema, - returnLastOnly: true, - }), + this.executeRegisteredQuery( + { queryId, params }, + { + schema: opts?.schema ?? this.schema, + returnLastOnly: true, + }, + ), ); } @@ -304,9 +317,16 @@ export class QueryEngine { for (const queryFn of queryFns) { const query = queryFn(); - // TODO: add appropriate error method if (!query.hash) { - throw new Error('Query to be registered is missing a hash!'); + throw SynthqlError.createMissingHashError({ + query, + }); + } + + if (this.queries.has(query.hash)) { + throw SynthqlError.createQueryAlreadyRegisteredError({ + queryId: query.hash, + }); } this.queries.set(query.hash, queryFn); diff --git a/packages/backend/src/SynthqlError.ts b/packages/backend/src/SynthqlError.ts index ee92390f..87cdf180 100644 --- a/packages/backend/src/SynthqlError.ts +++ b/packages/backend/src/SynthqlError.ts @@ -19,10 +19,6 @@ export class SynthqlError extends Error { Error.captureStackTrace(this, SynthqlError); } - // TODO: add appropriate static methods for the new errors in the - // QueryEngine: registerQueries, executeParameterizedQuery, and - // possibly, executeParameterizedQueryAndWait - static createCardinalityError() { const type = 'CardinalityError'; @@ -84,10 +80,54 @@ export class SynthqlError extends Error { return new SynthqlError(error, type, lines.join('\n')); } - static createMissingHashError() { + static createMissingHashError({ query }: { query: AnyQuery }) { const type = 'MissingHashError'; - const lines = ['A query passed to be registered, is missing its hash!']; + const lines = [ + 'Missing hash error!', + '', + 'The query:', + '', + JSON.stringify(query, null, 2), + '', + 'is missing its `hash` property, which is', + 'used as the key when registering it', + 'via QueryEngine.registerQueries()', + '', + ]; + + return new SynthqlError(new Error(), type, lines.join('\n')); + } + + static createQueryAlreadyRegisteredError({ queryId }: { queryId: string }) { + const type = 'QueryAlreadyRegisteredError'; + + const lines = [ + 'Query already registered error!', + '', + 'A query already exists in the query store for the queryId:', + '', + JSON.stringify(queryId, null, 2), + '', + ]; + + return new SynthqlError(new Error(), type, lines.join('\n')); + } + + static createQueryNotRegisteredError({ queryId }: { queryId: string }) { + const type = 'QueryNotRegisteredError'; + + const lines = [ + 'Query not registered error!', + '', + 'No query found in the query store for the queryId:', + '', + JSON.stringify(queryId, null, 2), + '', + 'Check and make sure the correct queryId', + '(i.e query.hash) is being passed', + '', + ]; return new SynthqlError(new Error(), type, lines.join('\n')); } diff --git a/packages/docs/static/reference/assets/search.js b/packages/docs/static/reference/assets/search.js index 87d1e2c8..0b3f7225 100644 --- a/packages/docs/static/reference/assets/search.js +++ b/packages/docs/static/reference/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file diff --git a/packages/queries/src/query.ts b/packages/queries/src/query.ts index eeaf9b8c..4e92b0b3 100644 --- a/packages/queries/src/query.ts +++ b/packages/queries/src/query.ts @@ -88,9 +88,6 @@ export class QueryBuilder< name: this._name, }; - // TODO: possibly wrap this logic in a wrapper function - // with a better descriptive name, and documentation - // Assigning identifiers for parameterized queries iterateRecursively(query, (x, path) => { if (isQueryParameter(x)) { x.id = path.join('.'); diff --git a/packages/queries/src/types/QueryRequest.ts b/packages/queries/src/types/QueryRequest.ts index 0c64ad06..a7576676 100644 --- a/packages/queries/src/types/QueryRequest.ts +++ b/packages/queries/src/types/QueryRequest.ts @@ -5,10 +5,10 @@ export interface RegularQueryRequest { query: AnyQuery; } -export interface RegisteredQueryRequest { +export interface RegisteredQueryRequest { type: 'RegisteredQuery'; queryId: string; - params: Record; + params: Record; } export type QueryRequest = RegularQueryRequest | RegisteredQueryRequest; diff --git a/packages/queries/src/util/iterateRecursively.test.ts b/packages/queries/src/util/iterateRecursively.test.ts deleted file mode 100644 index 21e0abf4..00000000 --- a/packages/queries/src/util/iterateRecursively.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { describe, test } from 'vitest'; -import { col } from '../col'; -import { param } from '../param'; -import { from } from '../generated'; - -describe('iterateRecursively', () => { - // TODO: complete test or remove it - test('Find one film_actor with `param()`', () => { - const q = from('film_actor') - .columns('actor_id', 'film_id', 'last_update') - .where({ - actor_id: param(1), - film_id: param(1), - }) - .include({ - film: from('film') - .columns('film_id', 'title') - .where({ - film_id: col('film_actor.film_id'), - language_id: param(1), - }) - .maybe(), - }) - .maybe(); - }); -}); diff --git a/packages/queries/src/util/iterateRecursively.ts b/packages/queries/src/util/iterateRecursively.ts index beec8914..257eb98f 100644 --- a/packages/queries/src/util/iterateRecursively.ts +++ b/packages/queries/src/util/iterateRecursively.ts @@ -18,8 +18,6 @@ type Traversable = | { [key: string | number]: Traversable } | Array; -// TODO: possibly rename to better, more descriptive, OR -// create specific named wrappers that use it internally export function iterateRecursively( traversable: T, visitor: (traversable: Traversable, path: string[]) => void, From c4fd1e39686b53d62a405e7b35e1921e41430663 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Wed, 18 Sep 2024 09:17:20 +0000 Subject: [PATCH 06/15] fix: added checker for undefined Signed-off-by: Jim Ezesinachi --- packages/backend/src/QueryEngine.ts | 12 ++++++-- packages/backend/src/SynthqlError.ts | 28 +++++++++++++++++++ .../docs/static/reference/assets/search.js | 2 +- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/QueryEngine.ts b/packages/backend/src/QueryEngine.ts index babfa0ec..e78ff9f8 100644 --- a/packages/backend/src/QueryEngine.ts +++ b/packages/backend/src/QueryEngine.ts @@ -237,8 +237,16 @@ export class QueryEngine { iterateRecursively(query, (x, path) => { if (isQueryParameter(x)) { - // TODO: possibly throw error if params?.[x.id]; is undefined? - x.value = params?.[x.id]; + const value = params?.[x.id]; + + if (value === undefined) { + throw SynthqlError.createMissingValueError({ + params, + paramId: x.id, + }); + } + + x.value = value; } }); diff --git a/packages/backend/src/SynthqlError.ts b/packages/backend/src/SynthqlError.ts index 87cdf180..38c6b600 100644 --- a/packages/backend/src/SynthqlError.ts +++ b/packages/backend/src/SynthqlError.ts @@ -99,6 +99,34 @@ export class SynthqlError extends Error { return new SynthqlError(new Error(), type, lines.join('\n')); } + static createMissingValueError({ + params, + paramId, + }: { + params: Record; + paramId: string; + }) { + const type = 'MissingValueError'; + + const lines = [ + 'Missing value error!', + '', + 'No value found for the parameter:', + '', + JSON.stringify(paramId, null, 2), + '', + 'in the `params` object:', + '', + JSON.stringify(params, null, 2), + '', + 'Check and make sure the correct value', + 'is included in the `params` object', + '', + ]; + + return new SynthqlError(new Error(), type, lines.join('\n')); + } + static createQueryAlreadyRegisteredError({ queryId }: { queryId: string }) { const type = 'QueryAlreadyRegisteredError'; diff --git a/packages/docs/static/reference/assets/search.js b/packages/docs/static/reference/assets/search.js index 0b3f7225..84baead2 100644 --- a/packages/docs/static/reference/assets/search.js +++ b/packages/docs/static/reference/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file From 772b1667646a6e143fd6e964eb8455d5ece6a13e Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Thu, 19 Sep 2024 17:25:52 +0000 Subject: [PATCH 07/15] updated client and handlers Signed-off-by: Jim Ezesinachi --- .../static/reference/assets/navigation.js | 2 +- .../docs/static/reference/assets/search.js | 2 +- .../src/createExpressSynthqlHandler.ts | 64 ++++++++++---- .../src/createNextSynthqlHandler.test.ts | 2 +- .../src/createNextSynthqlHandler.ts | 83 +++++++++++++++---- packages/queries/src/index.ts | 2 + packages/queries/src/types/QueryParameter.ts | 4 +- packages/queries/src/types/QueryRequest.ts | 7 +- .../src/validators/isQueryParameter.ts | 4 +- .../src/validators/isRegisteredQuery.ts | 5 ++ .../queries/src/validators/isRegularQuery.ts | 5 ++ packages/react/src/createBody.ts | 57 +++++++++++++ packages/react/src/useSynthql.ts | 5 +- .../react/src/useSynthqlExamples.test.tsx | 2 +- 14 files changed, 200 insertions(+), 44 deletions(-) create mode 100644 packages/queries/src/validators/isRegisteredQuery.ts create mode 100644 packages/queries/src/validators/isRegularQuery.ts create mode 100644 packages/react/src/createBody.ts diff --git a/packages/docs/static/reference/assets/navigation.js b/packages/docs/static/reference/assets/navigation.js index 17b69f28..efe3cb08 100644 --- a/packages/docs/static/reference/assets/navigation.js +++ b/packages/docs/static/reference/assets/navigation.js @@ -1 +1 @@ -window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE5WXbU/bMBCA/0s+lzHYYFu/lVIJtgm6FG2aEELGuRIP10ltB7Wa9t+nvDWJ7ZzN1+a5xy93vtr3fyMNOx1NoydCX0Akx0rSaBLlRKfRNNpkScFBHTcfH5Wk71K94dEkemEiiaank4imjCcSRDS9P8h+FCD3C/HMBHQyyolShqwHDsUnp5//TQ6+1V7odMsXUmYSF/ZJzNgbeCmzXHVWJjTINaHjM60CjH04OzflS05EmLUkMR3NOAeqvxOlO+G6EFSzTAx9PXRoPP84EG7yTEE1uN/YsZbyoS/lzF08lDNH4fQiUyISDvIIdrkEpdyWBnpsoOBSpBKIhkUd1VTHVe1yLd01DKJAd6Rdl4CdxhdVEsEruoGdHluI3ucurR1ijPT+y6eTs35O7IgYtgX0KzB4rCbSN2S9zdjq7DQdRh0LRhPEhJaZyoGOpKf7HpycZxAgiQbXpA1di6JT3BYgGYycieZj8OTmGS824hLWzr7Utx1IrC8doLIhgtQM3G3UKe5igoZ4gxkVxvDMlAYJSdXRrKIekbvDPAMVnMi3jmLGYEOsaAob4rXWGCa6I08cQsqiBTHZTOzrVJidoi86QL6mMBP7ywuP6fIiwGL82TlFjj85p6vaBo+rYkJdg70f19k7bxkvmCByf5tjupYJdZVdKrP+ZFzGmvR550QmTBDONJqQHuY1egsurNpqKoY1SBAUTbGBhpnv9nmAtKTCfD8JLwKEFeYzXgvKiwS1NYjP9DVjAq/AmvB5vEc26LzWV2wiyQa0fVWydAcyyDtyKbKsgVeghlUFD1GWmM8YwxpPRQX4LCsoXxWYpiZ8Hm/jDOqav1KQqKUCgixzTgrld9WYt19e38zi34+3y0U8u7uNV532lUhWrszomgY/9H8Y3I0z7rpR9m0048iTL/nzdHpFVOqztByiSolKR5+OgytpCyIypsbOp9tp8qjaKP4xo+MIDEW6uqfHQAup2Ctw78rtCESfl2vxGSsIkWxDElJBRyfog0MCGXsOVZ+CHxvNO2yeieoH19WyEw5h9NJbk1UVfAPr78FStqDv8KqR6XZHt1MPWXNDBwe3mcVSZq8scZe3NeUWRmt7dB9cYgtH1OotYgN21FYnLhQ0s8CdHRckszKGO50lVp2Bh/9Ohll4DRUAAA==" \ No newline at end of file +window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE5WX204bMRBA/2WfQym00JY3LpGgrYAmqFWFEDLeCevieDe2FyWq+u/V3nd9GZvX7PEZX8aT8f3fRMNWJyfJE6EvINJ9JWkySwqis+QkWedpyUHttx8flaTvMr3mySx5YSJNTg5nCc0YTyWI5OS+l/0oQe7m4pkJGGSUE6UM2Qicig8OP/+b9b7lTuhsw+dS5hIXjknMOAp8K/NCDVYmNMgVof6Z1gOMfTg6NuW3nIg4a0ViOppzDlR/J0oPwlUpqGa5mPpG6NR4/HEiXBe5gjp42DiwlvJhLOXMnTyUM0fijEZmRKQc5B5sCwlKuS0t9NhC0alIJRAN82ZUmx2Xjcu1dFcYRIHuSLcuAVuNL6oiold0DVvtW4jeFS6tPcSI9P7Lp4Oj8ZnYIxawKWGcgdGx2pGhkM02Y6uzj6mP6huMHhATWuaqAOo5nuF79OE8gwBJNLgmbeg6FJ3ipgTJwHMn2o/RkzvPebkWF7By1qWxrSexutRDVUEEqRm4y6hTPIyJCvEGMypcwDNTGiSkdUWzktojdw8LBCo5kW+NYo7BQixpBmsStDYYJrojTxxi0qIDMdmp2DVHYVaKsaiHQkXhVOwuzgKmi7MIi/Fn5xQ5/uScrnobAq6aiXVN9t6vs3feMp4xQeTupsB0HRPrqqpUbv3JuIwNGfKeE5kyQTjT6IGMsKAxmHBx2dZQC1iBBEHRIzbQOPPdroiQVlSc7yfhZYSwxkLGK0F5maK2FgmZvuZM4BnYECFP8MpG3demxSaSrEHbrZKl68kor6cpsqyRLVDLqpLHKCssZFzACj+KGghZllC9KjBNQ4Q8wcIZVTV/ZSBRSw1EWc45KVXY1WDBenl1fbr4/XhzO1+c3t0sloP2lUhWrcyomgY/9X849LctIbOB4+K+5Yiw9iyibBtwx5Vza00eUdOcuxrrsY3mHHn5pn+eDi+JykKWjkNUGVGZ9wU96cw7EJEx5StTbqfJo2qjBviMjkpgifDm2W8Od892KH/7jMTB++dpEF0/vxZAS6nYK/DgSdojEH1RnU3IWEOIZBOTYDW0d4C+IyUQ3yu3/hT9hmxv63ku6h9cL4ZBOIXRt0xD1kf3Dax/fUvZgaGarDzTHUrRoJ6y5oY6a5zMX1nqvq7WlDsYzXvvPrjEFo6o1VvEBuzIrUFcKmhngTsHLkpmnRjudKZYfQce/gOI0+6M5BYAAA==" \ No newline at end of file diff --git a/packages/docs/static/reference/assets/search.js b/packages/docs/static/reference/assets/search.js index 84baead2..9fcbc087 100644 --- a/packages/docs/static/reference/assets/search.js +++ b/packages/docs/static/reference/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,H4sIAAAAAAAAE71dW3PbuJL+K1vyPCoagXf6LbeqmbO7MzlO9pzacqVcjAQrnKFJmaSSeFP571sASbEb7OZNdF5OPEfo7g/9NRpAEyS+r/Lsa7G6vv2++jtO96tra71Kowe5ul59inZ/y3T/a5HvVuvVKU9W16uHbH9KZPFr/dtdke82n8uHZLVe7ZKoKGSxul6tfqy72nZJTGraJfEELZ+jdJ/I/IX8dsxlUZAa6zZ3dZsZ2lP5rexVrRpM0BunZZ4VR7mjtbY/T9D5eJJ5LGkP1L9N0JbLiAGnfxnU5Dkt0VmSyF35X1FRnpXdn9JdGWcpDhzQklC+Xh2jXKalEYmMzYdjVsh/nmT+NGi0bTrLquva3tnw3V35dJRTTV6h/+esYxAG1oQ63QIUW6t1TPGYvDrFyV7mS2DcIHUXwG36zKCOTocHmZZyP4tQEnlH5aLoLbcNCd3kXRKlZ9xxWsr8PtoZefPccE4YIn+9/SZ3J+WZ//gQP8hpdq/Owne18LBn2i4ygNRvaZwe5uBpZBeE816WZZweiolIgNg8EL25YhqGKVmiVXHuAeOYD3l8OMh8qmOA2ILBMjNOlgMxA8Bs452M8TY9xKl8l2fHcWyA9hfnD2Vtjs2rSnCkB2APuelq91k+RPOwnGWXg7OP0oPMs1ORPL1Mkuzr/6S5PMRFKfNqHonlPK6uxileriPHXB5lun//OJNnJL8krOxLvB+bfwhUrfiCoLJsrpcqyeWgJNnhMDYxdtC0wpcBsgIqUZ0x1co5QHNykyvAtjFLizI/7cosn2TyCgtOcsGoyBgFohMRZbF/ERcvjnn8JSrlBViMTDkKDZEhF8MzJVWOAjs1RS7WEyJXjuOazpGL4Xqc48XH5/SU1DuHLJ+ICYotg8raOgGqBcTJtAR11QrNzRS9a/0pGDb1vy/E5DV/rc1Q1FMbWAbgppi+LjBBDlQEjlEePUyMMxbuWdnCiFEYVlE+MQRaoblhSGF4me7/HcXlLCit7JKIblAqf5qFrKvjGRFe4kJW1UJ4j0kUp1ORNUKLYGhm5lkTfVd49mINLFXfP6Xl58fkbZ4PLBxhwzmLVTz35DIq5eso38dplMTl0wz7V6ySYb+gXvdifBOV0aeokK+zNJW6qjofKq9rScT/KLL0XZQXcXqYD5VQsiTG/44Lpfq3qPg8HyOh5Bkw/itKTvJikEjLkiirVJnkMto/tQl0Pt5+fYsj/yMrF0NN6loS8bvzzuX8LGA+5B5lS2K+kcUxSwv5vsxl9HBRTmBVLYl3Gecu6tU5ZRYC2MQ6CwMGbyzzqSgqiWWsD+7cuuZHb9PG2H+QRREdpkJopTp76jj9LPO4lPtLUO2y/VRItchsr8AH/Dr831YnOurmv1WHMYjHw9QJkB4N/cs+6sgJHOqhL9x2IP0hv5UMPhUkxBGSrsQ4POdDKiNrENPMXw3UHkwUhJ5JXrqRjycJToyMRlsLzveZGWQ9DHYj7AyMk10C10GmMo9K6piJcXSoadlv1TiOxJ3iIczBg0W7LOk3A08okTZ0HWbAim5zoZ1H5hQJtKPbvBCzLI07EcRZ0zXawVNAwHQlXf0vt+i4z7Mhz7IYNrXwJCBUNQyPeVzpqcY3xDPiRNYM//fZGcpxsKu9p6yQt0dYHO/hMaelCqlO0U2yfxZZBsHXzzKf5PJNI7GM/TjdJaf9NAStzDIYkvghnkZCI7GM/ez+vpDTAJxFlkGwaytWk2BguYXYiP5vGohaYBnrhzw7HV9NA9DKLIPhc1R8ngSgFljGuv5nivVaYLZ1c7Z5mT59iD4l8o2878EBWv2Emce0NmX+gf1hd2jJ6SEtpgPYtJKTgIxi4c2rfkBvXs3xPEf3iM4vZO619lm/varNQgaH1k9Nkznm7NbSq9//eHnzv3d/vnt78/LDnzfvzxa/RHms/Ietms2X6OyrOI3ypz+PaiuTmftnZBw1XNL0CKNLmHs9asoErRYxOhS5y4VtpelG3stcpru+0Wm0XM64fkIxaFi3Ws7oh/6JoG20hMnfB1e/dYsljP0ji9Pe4VE1WMJUdWxa7f9l2SmjdVYT54Y/awuJDU7eS7YdG1OGnoJhMxPI0CZrPw+NllsYy5eBQd0DpxG9HBE8wn8jD6ck0mcYnsxSJji1CzESInNCl4+ZiXb7H2JAx1Cd7Tm0+DQbUSN9KSSDKXgaZzxZhNTP4oszPYkyqtd9rP2+vwRZq2JpcMZRwBnYBs7/TYJGTlr0w4xOgrogihizxSkZYVW1WsLojbzvXQro33/CdNzamTILV+iZIPsl760bdCxu7iqBkWa7k8mlXdYAarUvxGQkG91hLksN7KwHwGwa+RmQhkqOQ1uZIWhnBctjy8YNDRqXFl4EE5z73rOv10FsVauL57Zf+Jf5GGtXdwPv8MGu1325YGKFlicMm37De1ns8vioHrdNsQ/EFoFxzLOjzEvmBUUORSu1CIhcPp7iXA6uIyCERmYRANF+HyuXRsm7Wf6g5BcB9ste3k9CclcLzDA89iX0XtNTHk/XwlUfnwfH5u4uTvfy27QJDwIzFI3Y9F4KcexGuAflwIyzqEfHVxHmA56ZLPtxY6XPCX9eku1Hj3Q+J/g5ybkfOtD4nMDvs/whGtywj4d91vecoC+difq7wGh/1lwTl8mSyaZW96yQiw/LJshG33OCfojT+OH0sBzqVuGzwo6+LQz7rPA5Yct0Scy1toUBww1d54gDg/mSQw6Tlxj4iMHoZcTQ8YYJiwWMYOqCYAjI+Gkf45g4tQ/BGDuBYxCTJukhCHNmNQxn9sxFQIMDo3rC+mbELqttefHgMA/gjLN4NeX0DejXZTsrBsSUfR5Q0XThgqzRD2j8bqSLarFV/ADEieN7MtCxA34A5qQMMBnknJQwAHh2jhgDnkwaowH/lPkUWxs/obbduXxGNTBMnVIHocwYhLMm1UEgkwfZjGl1EMRFg+jCiZUCRw6SGdBakZ87cAy7M4YQ6GzvWxjoSdlUWEjFouCqlx0uAodULApOvwtyETaoYVFo6SlJLkIGFCxLaPEujx+i/Ok/5eDxGp5SrORSgObxgPdD7yVVDZY4iTB09nuxg9//HnjTSf++mKHXSXQqBs1VrS58RzIumOOW9MuDZvOLreOzJJzR+SdK8Isx+Cw9be3c7kJ7+78+Wb/Bl3Foc02zSz1Z6heAb+TulBfxF5kMdbMrcCGCUyHrV6AJy+1tAm2zfnvtzQSktaLSoYmCuZAyabQdett3wHLcfEVglO1O6+m2O/mVMV+lidb0xYZBp2td7+ov7/Z22Wh7kd02Wl5naSm/UVdLULFVt57hbHhshrYMpttOp2fbNcrR+2MWp9NNXgHJAduGeN++SBbl72k8Aw0Wng0IvKVU0JS07yh1Rn3ddMKg/7heVWcDrr+vvsi8UJvd65W1sTfhar26j2WyVxfnNO867rIHdcPE6mP927/kTn/k9Pq2avLrdrW+3a6dYBPa4uPH9W0joX/Q/4duJlbrW0E1E6iZtVrfWmtbbESwtte2swmcEDW3UHMbNHeo5jZq7qzWty4FwkHN3NX61qOauaiZt1rf+lQzDzXzV+vbgGrmo2bBan0bUs0C1CxUftyuLW/j+zZqF2J/K/cL2uMGM5oai2yJyRHK+cImW2JehPK7aBhZC7fiCItgboTioGm4Fh4lgWkSig5BMiAwU8JjvSYwWUKRIki6BOZLKF6Et3bsje/joBOYMqE5I7kVmDRLMWORo8nCpFmKGYuk1zJGlB5S1tr2N4Fr4ZaYNEuPJpvqkYW5shQRlkO2xBxZigiLHHMW5shSRFikPy3MkaWIsEjeLcyRpYiwSDYtzJGliLBC0jrmyN6yOG3MkS1Yz9uYI9tiPW8bCc9mPW9jjmxFhE3Gko05shURNhlLNubIVkTYZKqwMUe2z444G3NkKyJsMqnYmCNbEWHTfcccOYoIm870mCNHEWGTyd7BHDmKCJuMOgdz5CgibDLqHGM+0hyRmcHBHDmKCIdk08EcOYoIh2TTwRw5igiHZNPBHDmKCIfkyMEcOYoIxyFbYo5cRYRDcuRijlxFhENPyJgjVxHhkBy5mCNXEeGQHLmYI1cR4ZAcucayQRHhkhy5mCNXEeGSHLmYI9dn85KLOXIVES7Jpos5chURrr22wo299XFLzJGniHBJNj3MkaeIcEk2PcyRp4hwSTY9zJGniHDpFRbmyHPYbONhjjzNEcm7Z6zuNEck7x7myFNEeCTvHubIU0R4JO8e5shTRHgkmx7myFdEePba2W7swMMLTMyRL1gv+ZgjXxHhkZnWxxz5Nm8dc+TzHPmYI9/lrWOOfEWE55ItjUW45oictX3Mka858smWmCNfc0Qv7TFHgeaIXt1jjgJFhL+lrAeYo0AR4ZOxFGCOAkWET8ZSgDkKFBE+mecDzFGgiPDJzBBgjgK9TSIzQ4A5Cvg1Q2DslRQRPslmgDkKFBE+mUMCzFGoiPBJNkPMUag5ItkMMUehIiIgM0OIOQoVEQHJZog5ChURAclmiDkKFREByWaIOQo9NupCzFGod7Pk2AwxR6EiIiB5D40trSIiIGeE0NzUKiYCesu3Nba1W8EO5Oo32FYTRW/7tsbWdqupordzW2NPu+WTXvUbbOuy03L1G2yrWAlJwqrfYFvFS8hUAow97VZXIehawNbY1W75ZUT1G2gr+ElKdMoRipuQrjKYBQlddmAwmCUJXW8IyZQlzFqErjTQWw1hViF0rSEkA12YdQhdbQjJUBdmJULXG0I61s1aRFWMoOPMrEbomkNIx7pRjxC66sDoNSoSQtcdQnKpKiyzkGSxaUQYVQmhaw9iS2ZRYRQmRFWZIPfcwihNiKo2wbQ1iNMlCCbQjPKEsKp5TNCKDeasijlrbbubwDLqXUaRQlhV9c+mGxvc2RV3DtnYKFUIu1ob0lFsVCuErkkwUWGbhUC7p61Bnq5LMFnNqFkIuydbGlULYXs9hBiFC1FXLmhCjNqFsIMeQozyhbAr9ujRb1QwhNPHnlHEEE7FHp0rjDqGcCr26ARglDKEU9FHz3aOWcHVs52gR6pR0BC6bCGYCrVR0xC6ciGYIrVR1hC6eMGEhlHYEE7QExpGbUM4YU9oGOUN4W57QsOocAhX9LBtFDmELmUIpg5v1DmErmbQS2VhVDqE27Nacc3ae8/4M6odwvV6/GYUPITr9/nNoM8N+vxm0KeLG0LQKwCj8iGq0gfdP6P2ITzRE0RG+UN4Vo8zjAqI8OweZxhFEFFXQWhnGHUQ4VXDj074nvn4pGf2M4ohQpc8hKCznFEPEbrqIZhnOEZJRHgVf3TiMqoiwq+ef9GJyyiMiKoyQj94EUZtRFTFEbLoIIzqiNA1EGZNZNRHhK6C0CUKYVRIhK6DCPr5jzCKJEKXQoRFJ0/ffP6l6bPo5GKUSoQuiAiLHlFGtUTomoignwUJo2AidFlEWHQUGTUTEVT00VFklE2ELo6oO9XIB3cGf7o+Iiw6ioziidAlElazwaCukgj6+YwwSihCF0oE/YhGGFUUUZVR1FMaCob5EFMzSD9+EUYtReiKibBpuo1yitBFEw5GXVHRZxG+yLyU+9+rMwm3t+c3fb6v6o8YXIttc0Li+ypcXX//sV4Jr/rX9qt/Paf61xf1v/V/B/Xvardd/SGC+g9rW/9hW80fdv2Hqxv/aE8/qP9SHWjPn8MT+hCp00JVi+xKn9P84YrmjwaFx5pKn5rP+LT6/RB4QvCS+09Iym+lgpCXeqzOD7aCAXS8xQvWp7uBxQAIbgcE9+rVFCDrArQeJ3s6qP+W+w7m9vqn7yvBuQheeghkQXc50J/0d3LvsvrrvIj9QIBe270K1JeQgKANBN0Bweb7wUAcdDnk4gl9jx54G/Q5qKMz5Ly+S2LTZaDLnLOrC5NaERcQ7PFCCZTxgB0v4GXU0eskUh+pA+5pRX1e0hhpARjIYZNELG4MVPJGIKulZusclxt4Z1kmpQjgLrUyG9BiCNsQQj/8vP2gMfADsB72OL561xPElQfiqkl2LjcoKgVm3g9A3lLl3T7ZL9UHUIEwABDyvq8vyG7lbDCYbHYgZA/HrJCd5APSM+8rcLNcK2kBUaue5Fw2XPW1TGBEy+rWvFadA3rh9KvZ1/ec7s73nHa1gShy2I5pbfV9YvXxxM/NVVNg9ANePDYgta6/iiw9VteadiGBseWw9Go1D9Wlnurke1cNCG+XjTCoRgdaVw/olcvmNK1H3YrV4x4QBx4bflrRsbqTsnhMZHN7YhcXUMeuaip1Opij6i7R/PyB0K5GMCj5hNJqTLOyTxtIznx20Nry+kLLornQsqPMBXOZyyZKrazXaS6YbFyOg32kEGSnInmKkiT7ekrbbtZvE6DxDYaQVa8zbc59nTS+hXOAzcUXen8VSsNJ2momM9trVqfntSir+K9PVnWRClxnQUw+NwTbI9tA1IazUsAllOpDI3Ayg/1wOIL1pZmATJAJXdZUc0M9mAdA9xwuM9RyUbr/qi8/B+IgITicX2txHDhoQrHBaHO4cKHVUKDAaHO40XYeF8j3aF/TK2ksim1Ams1y1tzcDuRA5nK4zAVvr4SyYKwxks33seAw8WAfuYCuLjkDK1PgGb/eSfpcP9s7D4ECkLU8bqrW9yh9wit3ENgBRwkxw1iGc9YrXhgPeR/4NeAoUULEpgzGj88NQ/UtQbCYAo5RT7XrtMUZhq/ywqQBF5Hs1uF8bxrgBQj6TYrcckPZuH0SJiAQx5xwcaxeWf1bGm6DLvBZ24V2+LF9yxGqgPthn5vN4iKX93hTKuCUpUp/nGS9mNEYOh0IYabnAq1+Wy+Hr/dBJXBgBlwu/iuLU2NbHcAA4jJBdUcaiHKQsAJWqLrlDkQL3GVyuSPJDoc4PaDRCKLM4nx8/kwYjGsYGmyCPN+fDOIRDEWPc+b5e2owP0Iu2elDt4DuBOYCLi+eb4qFS0Mwb/SI9SylgWu98Sry5jP3QBMgl92Bty/FQ5agz3zO282VhSCc4E6SFcPjdQsjwuI6XF9PC0yBBOFxkdtcLgDmWMhPU5UVXBwekwivJ6BfWKNJlKbGeBEwFzQ1XS6rHzNcRYKJxGoqyJx32+0VUgG6bdX2bdbXTJEYpmSrmVqaUrZwGm+6zZzH7pOP9VuoeKsBZhyLi1Zii2KD+GFnWfiiNAwisMjukUSJ1gPzi19XyP2mBC84WrQamR7iFI00CwxRi4sHIKuowV4Dvbd6jRurFLiyYZfG/AQdwJGw7Zc3BxGcoLkQ0ZJETgvh9k30kpbXN3AAYTgKBbeoaN86hfMByDasHKqjhnAaZ5NFZ/0SwoWf4KZWY9NEOQp6mX2i0OghxpUDIovdtebVVT8sCJgytlx814JxGhv7bRij7BOg9mtPMFehGGsyk32u6J6fbDXZi51pm1sQwJCDW5FmL9I8IWuSq2BTWHPdM8goIKb9Jnn63MiAHyaCUzaMbXZDVsiyjNMDTuww4ljUeD6BpRCb47V4TD6d4mRvLO1hOYTLWPXaZte8Ug6jAsbU+UEp+7iu1tQt4QEYDjeka+Hj+QsLEAdct7BLxN5dBswQQfM4l92idjgPYQ62mgj0uUimnloK9NyR9UMZ49UxerjTzH0s7upz0nB0wohjy0xlHh8OxiJBQJdxQV4W3UfxUI6tSJliLohyt0kiW7f547x8bKizzrv9po3TxKfb/D9ek3rYMrn6egJMNyBQLc5Xp0IWzXdeYIRBRwfDwuR4g+OVfbLdeZAVomHOUVVfPQ9yIUhjfuM8nwst8K0xGNOQbHZvoGV39UedoM8gcLok9nG9OsZHmajF3PXtxx8//h/gwdRXuakAAA=="; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file diff --git a/packages/handler-express/src/createExpressSynthqlHandler.ts b/packages/handler-express/src/createExpressSynthqlHandler.ts index 88fd7dab..f153c5b0 100644 --- a/packages/handler-express/src/createExpressSynthqlHandler.ts +++ b/packages/handler-express/src/createExpressSynthqlHandler.ts @@ -1,5 +1,9 @@ -import { collectLast, QueryEngine, SynthqlError } from '@synthql/backend'; import type { Request, Response, RequestHandler } from 'express'; +import { collectLast, QueryEngine, SynthqlError } from '@synthql/backend'; +import { + isRegisteredQueryRequest, + isRegularQueryRequest, +} from '@synthql/queries'; /** * Create an Express request handler that can handle SynthQL requests. @@ -37,6 +41,7 @@ export function createExpressSynthqlHandler( error: e.message, }), ); + res.end(); } else { // Let another layer handle the error @@ -52,38 +57,52 @@ async function executeSynthqlRequest( res: Response, ) { // First try to parse the request body as JSON - const { query, returnLastOnly } = await tryParseRequest(req); + const { body, headers } = await tryParseRequest(req); // We don't do this yet, but eventually we'll want to validate the request // const validatedQuery = await tryValidateSynthqlQuery(query); // Execute the query, but just to get the initial generator - const resultGenerator = await tryExecuteQuery( - queryEngine, - query, - returnLastOnly, - ); + const resultGenerator = isRegisteredQueryRequest(body) + ? await tryExecuteRegisteredQuery( + queryEngine, + { queryId: body.queryId, params: body.params }, + headers.returnLastOnly, + ) + : isRegularQueryRequest(body) + ? await tryExecuteQuery( + queryEngine, + body.query, + headers.returnLastOnly, + ) + : await tryExecuteQuery( + queryEngine, + body, + headers.returnLastOnly, + ); // Now that we have the generator, we want to iterate over the items // and depending on `returnLastOnly`, we will write the status code // either before, or after iteration - await writeBody(res, query, resultGenerator, returnLastOnly); + await writeResponseBody(res, body, resultGenerator, headers.returnLastOnly); + // End response stream res.end(); } async function tryParseRequest(req: Request) { - const body = req.body; - const returnLastOnly = req.headers['x-return-last-only'] === 'true'; - try { - const query = JSON.parse(body); - - return { query, returnLastOnly }; + return { + body: JSON.parse(req.body), + headers: { + ...req.headers, + returnLastOnly: req.headers['x-return-last-only'] === 'true', + }, + }; } catch (e) { throw SynthqlError.createJsonParsingError({ error: e, - json: body, + json: req.body, }); } } @@ -96,7 +115,18 @@ async function tryExecuteQuery( return queryEngine.execute(query, { returnLastOnly }); } -async function writeBody( +async function tryExecuteRegisteredQuery( + queryEngine: QueryEngine, + { queryId, params }: { queryId: string; params: Record }, + returnLastOnly: boolean, +) { + return queryEngine.executeRegisteredQuery( + { queryId, params }, + { returnLastOnly }, + ); +} + +async function writeResponseBody( res: Response, query: any, generator: AsyncGenerator, @@ -126,7 +156,7 @@ async function writeBody( // First, wrap the error in a SynthqlError to capture // the fact that it happened during streaming - // The `e` can be of any type, but in case its an error, + // The `e` can be of any type, but in case its an error // we want to preserve the stack trace and any other // information that might be useful for debugging diff --git a/packages/handler-next/src/createNextSynthqlHandler.test.ts b/packages/handler-next/src/createNextSynthqlHandler.test.ts index eb7cb714..3a5da045 100644 --- a/packages/handler-next/src/createNextSynthqlHandler.test.ts +++ b/packages/handler-next/src/createNextSynthqlHandler.test.ts @@ -304,7 +304,7 @@ describe('createNextSynthqlHandler', () => { expect(async () => await handler(newReq)).not.toThrow(); }); - // TODO: FIX:This test does not yet accurately test for the response streaming error + // TODO: FIX: This test does not yet accurately test for the response streaming error test.skip(`Well-formed but invalid query object returns expected response streaming error`, async () => { const q = fromWithVirtualTables('film_rating') .columns('film_id', 'rating') diff --git a/packages/handler-next/src/createNextSynthqlHandler.ts b/packages/handler-next/src/createNextSynthqlHandler.ts index fbc165a0..c83dcbdc 100644 --- a/packages/handler-next/src/createNextSynthqlHandler.ts +++ b/packages/handler-next/src/createNextSynthqlHandler.ts @@ -1,6 +1,10 @@ -import { ReadableStream } from 'stream/web'; import { NextRequest, NextResponse } from 'next/server'; import { collectLast, QueryEngine, SynthqlError } from '@synthql/backend'; +import { + isRegisteredQueryRequest, + isRegularQueryRequest, +} from '@synthql/queries'; +import { ReadableStream } from 'stream/web'; export type NextSynthqlHandlerRequest = Pick< NextRequest, @@ -10,6 +14,23 @@ export type NextSynthqlHandler = ( req: NextSynthqlHandlerRequest, ) => Promise; +/** + * Create an Next request handler that can handle SynthQL requests. + * + * Usage: + * + * ```typescript + * import { createNextSynthqlHandler } from '@synthql/handler-next'; + * import { queryEngine } from './queryEngine'; + * + * const nextSynthqlRequestHandler = createNextSynthqlHandler(queryEngine); + * + * export async function POST(request: Request) { + * return await nextSynthqlRequestHandler(request); + * } + * ``` + * + */ export function createNextSynthqlHandler( queryEngine: QueryEngine, ): NextSynthqlHandler { @@ -46,33 +67,52 @@ async function executeSynthqlRequest( req: NextSynthqlHandlerRequest, ): Promise { // First try to parse the request body as JSON - const { query, returnLastOnly } = await tryParseRequest(req); + const { body, headers } = await tryParseRequest(req); // We don't do this yet, but eventually we'll want to validate the request // const validatedQuery = await tryValidateSynthqlQuery(query); // Execute the query, but just to get the initial generator - const resultGenerator = await tryExecuteQuery( - queryEngine, - query, - returnLastOnly, - ); + const resultGenerator = isRegisteredQueryRequest(body) + ? await tryExecuteRegisteredQuery( + queryEngine, + { queryId: body.queryId, params: body.params }, + headers.returnLastOnly, + ) + : isRegularQueryRequest(body) + ? await tryExecuteQuery( + queryEngine, + body.query, + headers.returnLastOnly, + ) + : await tryExecuteQuery( + queryEngine, + body, + headers.returnLastOnly, + ); // Now that we have the generator, we want to iterate over // the items and depending on `returnLastOnly`, we will // write the status code either before, or after iteration - return await writeBody(query, resultGenerator, returnLastOnly); + return await writeResponseBody( + body, + resultGenerator, + headers.returnLastOnly, + ); } async function tryParseRequest(req: NextSynthqlHandlerRequest) { const body = await req.text(); const requestHeaders = Object.fromEntries(req.headers); - const returnLastOnly = requestHeaders['x-return-last-only'] === 'true'; try { - const query = JSON.parse(body); - - return { query, returnLastOnly }; + return { + body: JSON.parse(body), + headers: { + ...req.headers, + returnLastOnly: requestHeaders['x-return-last-only'] === 'true', + }, + }; } catch (e) { throw SynthqlError.createJsonParsingError({ error: e, @@ -89,7 +129,18 @@ async function tryExecuteQuery( return queryEngine.execute(query, { returnLastOnly }); } -async function writeBody( +async function tryExecuteRegisteredQuery( + queryEngine: QueryEngine, + { queryId, params }: { queryId: string; params: Record }, + returnLastOnly: boolean, +) { + return queryEngine.executeRegisteredQuery( + { queryId, params }, + { returnLastOnly }, + ); +} + +async function writeResponseBody( query: any, generator: AsyncGenerator, returnLastOnly: boolean, @@ -131,14 +182,14 @@ async function writeBody( // we want to preserve the stack trace and any other // information that might be useful for debugging + // We need to catch errors here and write them to the streaming response + // We can't throw them because that would break the stream + const error = SynthqlError.createResponseStreamingError({ error: e, query, }); - // We need to catch errors here and write them to the streaming response - // We can't throw them because that would break the stream - return new NextResponse( JSON.stringify({ type: error.type, diff --git a/packages/queries/src/index.ts b/packages/queries/src/index.ts index 9a67a7f1..72c1eb63 100644 --- a/packages/queries/src/index.ts +++ b/packages/queries/src/index.ts @@ -18,6 +18,8 @@ export * from './types/Where'; export * from './types/WhereClause'; export * from './validators/isQueryParameter'; export * from './validators/isRefOp'; +export * from './validators/isRegisteredQuery'; +export * from './validators/isRegularQuery'; export * from './util/hashQuery'; export * from './util/iterateRecursively'; export { col } from './col'; diff --git a/packages/queries/src/types/QueryParameter.ts b/packages/queries/src/types/QueryParameter.ts index e13ef8c1..1b90d9e5 100644 --- a/packages/queries/src/types/QueryParameter.ts +++ b/packages/queries/src/types/QueryParameter.ts @@ -1,5 +1,7 @@ +export const SynthqlParameter = 'synthql::parameter'; + export type QueryParameter = { - type: 'synthql::parameter'; + type: typeof SynthqlParameter; id: string; value: TValue | undefined; }; diff --git a/packages/queries/src/types/QueryRequest.ts b/packages/queries/src/types/QueryRequest.ts index a7576676..abff8f4b 100644 --- a/packages/queries/src/types/QueryRequest.ts +++ b/packages/queries/src/types/QueryRequest.ts @@ -1,12 +1,15 @@ import { AnyQuery } from './AnyQuery'; +export const RegularQuery = 'RegularQuery'; +export const RegisteredQuery = 'RegisteredQuery'; + export interface RegularQueryRequest { - type: 'RegularQuery'; + type: typeof RegularQuery; query: AnyQuery; } export interface RegisteredQueryRequest { - type: 'RegisteredQuery'; + type: typeof RegisteredQuery; queryId: string; params: Record; } diff --git a/packages/queries/src/validators/isQueryParameter.ts b/packages/queries/src/validators/isQueryParameter.ts index 36f7b168..95d5987e 100644 --- a/packages/queries/src/validators/isQueryParameter.ts +++ b/packages/queries/src/validators/isQueryParameter.ts @@ -1,5 +1,5 @@ -import { QueryParameter } from '../types/QueryParameter'; +import { QueryParameter, SynthqlParameter } from '../types/QueryParameter'; export function isQueryParameter(x: any): x is QueryParameter { - return x !== null && x !== undefined && x?.type === 'synthql::parameter'; + return x !== null && x !== undefined && x?.type === SynthqlParameter; } diff --git a/packages/queries/src/validators/isRegisteredQuery.ts b/packages/queries/src/validators/isRegisteredQuery.ts new file mode 100644 index 00000000..c69bd238 --- /dev/null +++ b/packages/queries/src/validators/isRegisteredQuery.ts @@ -0,0 +1,5 @@ +import { RegisteredQuery, RegisteredQueryRequest } from '../types/QueryRequest'; + +export function isRegisteredQueryRequest(x: any): x is RegisteredQueryRequest { + return x !== null && x !== undefined && x?.type === RegisteredQuery; +} diff --git a/packages/queries/src/validators/isRegularQuery.ts b/packages/queries/src/validators/isRegularQuery.ts new file mode 100644 index 00000000..990ef0a5 --- /dev/null +++ b/packages/queries/src/validators/isRegularQuery.ts @@ -0,0 +1,5 @@ +import { RegularQuery, RegularQueryRequest } from '../types/QueryRequest'; + +export function isRegularQueryRequest(x: any): x is RegularQueryRequest { + return x !== null && x !== undefined && x?.type === RegularQuery; +} diff --git a/packages/react/src/createBody.ts b/packages/react/src/createBody.ts new file mode 100644 index 00000000..4bef2621 --- /dev/null +++ b/packages/react/src/createBody.ts @@ -0,0 +1,57 @@ +import { + AnyQuery, + isQueryParameter, + iterateRecursively, + QueryRequest, +} from '@synthql/queries'; + +export function createBody(query: AnyQuery): QueryRequest { + const params: Record = {}; + + iterateRecursively(query, (x, path) => { + if (isQueryParameter(x)) { + if (x.value === undefined) { + throw new Error( + [ + 'Missing value error!', + '', + 'No value passed for the parameter:', + '', + JSON.stringify(x.id, null, 2), + '', + ].join('\n'), + ); + } + + params[x.id] = x.value; + } + }); + + if (query.name || Object.keys(params).length > 0) { + if (!query.hash) { + throw new Error( + [ + 'Missing hash error!', + '', + 'The query:', + '', + JSON.stringify(query, null, 2), + '', + 'is missing its `hash` property', + '', + ].join('\n'), + ); + } + + return { + type: 'RegisteredQuery', + queryId: query.hash, + params, + }; + } else { + return { + type: 'RegularQuery', + query, + }; + } +} diff --git a/packages/react/src/useSynthql.ts b/packages/react/src/useSynthql.ts index c99c8a7e..841ad942 100644 --- a/packages/react/src/useSynthql.ts +++ b/packages/react/src/useSynthql.ts @@ -1,8 +1,9 @@ +import { QueryOptions, UseQueryResult } from '@tanstack/react-query'; import { Query, QueryResult, Table } from '@synthql/queries'; import { useSynthqlContext } from './SynthqlProvider'; import { useAyncGeneratorQuery } from './useAsyncGeneratorQuery'; import { synthqlQueryKey } from './synthqlQueryKey'; -import { QueryOptions, UseQueryResult } from '@tanstack/react-query'; +import { createBody } from './createBody'; import { fetchJsonLines } from './fetchJsonLines'; type SynthqlQueryOptions< @@ -34,7 +35,7 @@ export function useSynthql< ...requestInit?.headers, 'X-Return-Last-Only': opts.returnLastOnly ? 'true' : 'false', }, - body: JSON.stringify(query), + body: JSON.stringify(createBody(query)), }; const queryKey = synthqlQueryKey(query, { diff --git a/packages/react/src/useSynthqlExamples.test.tsx b/packages/react/src/useSynthqlExamples.test.tsx index 7fc768a4..2956ef33 100644 --- a/packages/react/src/useSynthqlExamples.test.tsx +++ b/packages/react/src/useSynthqlExamples.test.tsx @@ -11,7 +11,7 @@ describe('useSynthql test examples', () => { beforeAll(async () => { echoServer = await createEchoServer((req) => { - return Object.values(req.where?.id.in).map((id) => { + return Object.values(req.query.where?.id.in).map((id) => { return { id, name: 'Bob', age: 1, active: true }; }); }); From b048b669acdd065291ed349990fc40c49c09eb35 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Thu, 19 Sep 2024 18:00:41 +0000 Subject: [PATCH 08/15] fixes Signed-off-by: Jim Ezesinachi --- packages/handler-express/src/createExpressSynthqlHandler.ts | 4 +++- packages/handler-next/src/createNextSynthqlHandler.ts | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/handler-express/src/createExpressSynthqlHandler.ts b/packages/handler-express/src/createExpressSynthqlHandler.ts index f153c5b0..bcffd21e 100644 --- a/packages/handler-express/src/createExpressSynthqlHandler.ts +++ b/packages/handler-express/src/createExpressSynthqlHandler.ts @@ -13,6 +13,7 @@ import { * ```typescript * import express from 'express'; * import { createExpressSynthqlHandler } from '@synthql/handler-express'; + * import { queryEngine } from './queryEngine'; * * const app = express(); * app.use(createExpressSynthqlHandler(queryEngine)); @@ -93,7 +94,8 @@ async function executeSynthqlRequest( async function tryParseRequest(req: Request) { try { return { - body: JSON.parse(req.body), + body: + typeof req.body === 'string' ? JSON.parse(req.body) : req.body, headers: { ...req.headers, returnLastOnly: req.headers['x-return-last-only'] === 'true', diff --git a/packages/handler-next/src/createNextSynthqlHandler.ts b/packages/handler-next/src/createNextSynthqlHandler.ts index c83dcbdc..75dd7da1 100644 --- a/packages/handler-next/src/createNextSynthqlHandler.ts +++ b/packages/handler-next/src/createNextSynthqlHandler.ts @@ -15,7 +15,7 @@ export type NextSynthqlHandler = ( ) => Promise; /** - * Create an Next request handler that can handle SynthQL requests. + * Create a Next request handler that can handle SynthQL requests. * * Usage: * @@ -109,7 +109,7 @@ async function tryParseRequest(req: NextSynthqlHandlerRequest) { return { body: JSON.parse(body), headers: { - ...req.headers, + ...requestHeaders, returnLastOnly: requestHeaders['x-return-last-only'] === 'true', }, }; From fd284ea3653b9c7db37076e48c8841840178dcda Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Fri, 20 Sep 2024 17:53:40 +0000 Subject: [PATCH 09/15] updated docs and minor logic improvements Signed-off-by: Jim Ezesinachi --- packages/backend/src/QueryEngine.ts | 4 ++-- packages/docs/docs/200-security.md | 8 ++++---- packages/docs/static/reference/assets/search.js | 2 +- packages/queries/src/types/QueryParameter.ts | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/backend/src/QueryEngine.ts b/packages/backend/src/QueryEngine.ts index e78ff9f8..e7ab3a7d 100644 --- a/packages/backend/src/QueryEngine.ts +++ b/packages/backend/src/QueryEngine.ts @@ -155,9 +155,9 @@ export class QueryEngine { }); } - const queryFn = this.queries.get(query.hash); + const hasQueryFn = this.queries.has(query.hash); - if (!queryFn) { + if (!hasQueryFn) { throw SynthqlError.createQueryNotRegisteredError({ queryId: query.hash, }); diff --git a/packages/docs/docs/200-security.md b/packages/docs/docs/200-security.md index 08d96067..4fea0bc3 100644 --- a/packages/docs/docs/200-security.md +++ b/packages/docs/docs/200-security.md @@ -18,15 +18,15 @@ const users = from('users').columns('id', 'name', 'email'); const queryEngine = new QueryEngine(opts); -queryEngine.registerQueries(users); +queryEngine.registerQueries([users]); ``` What this means is that the `QueryEngine` will only allow queries on the `users` table and will allow any subset of the `id`, `name` and `email` columns to be selected. -This behaviour can be disabled with the `allowUnknownQueries` option. +This behaviour can be disabled with the `dangerouslyAllowUnregisteredQueries` option. ```ts -const queryEngine = new QueryEngine({..., allowUnknownQueries:true}); +const queryEngine = new QueryEngine({..., dangerouslyAllowUnregisteredQueries: true}); ``` ## Restricting access to tables and columns @@ -119,5 +119,5 @@ const queryEngine = new QueryEngine({ middlewares: [restrictOrdersByUser], }); -queryEngine.registerQueries(orders); +queryEngine.registerQueries([orders]); ``` diff --git a/packages/docs/static/reference/assets/search.js b/packages/docs/static/reference/assets/search.js index 9fcbc087..d9cb74ba 100644 --- a/packages/docs/static/reference/assets/search.js +++ b/packages/docs/static/reference/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file diff --git a/packages/queries/src/types/QueryParameter.ts b/packages/queries/src/types/QueryParameter.ts index 1b90d9e5..bac3387d 100644 --- a/packages/queries/src/types/QueryParameter.ts +++ b/packages/queries/src/types/QueryParameter.ts @@ -2,6 +2,6 @@ export const SynthqlParameter = 'synthql::parameter'; export type QueryParameter = { type: typeof SynthqlParameter; - id: string; value: TValue | undefined; + id: string; }; From 1401f860a54e9dbb68d8c662b38095ecc631fe53 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Fri, 20 Sep 2024 19:57:21 +0000 Subject: [PATCH 10/15] more tests, fixes & refactors Signed-off-by: Jim Ezesinachi --- packages/backend/src/QueryEngine.test.ts | 56 +++++++---------- packages/backend/src/QueryEngine.ts | 2 +- packages/react/src/createBody.test.ts | 79 ++++++++++++++++++++++++ packages/react/src/createBody.ts | 4 +- packages/react/src/useSynthql.ts | 3 +- 5 files changed, 107 insertions(+), 37 deletions(-) create mode 100644 packages/react/src/createBody.test.ts diff --git a/packages/backend/src/QueryEngine.test.ts b/packages/backend/src/QueryEngine.test.ts index 251fe8a5..7fc34b79 100644 --- a/packages/backend/src/QueryEngine.test.ts +++ b/packages/backend/src/QueryEngine.test.ts @@ -3,65 +3,57 @@ import { col, param } from '@synthql/queries'; import { queryEngine } from './tests/queryEngine'; import { from } from './tests/generated'; +const params = { + 'where.actor_id': 2, + 'where.film_id': 47, + 'include.film.where.language_id': 3, + 'include.film.include.language.where.last_update': '2022-02-15 10:02:19+00', +}; + describe('QueryEngine', () => { it('registerQueries + executeRegisteredQuery', async () => { - queryEngine.registerQueries([findFilmActor]); - - const params = { - 'where.actor_id': 2, - 'where.film_id': 47, - 'include.film.where.language_id': 3, - 'include.film.include.language.where.last_update': - '2022-02-15 10:02:19+00', - }; + queryEngine.registerQueries([() => findFilmActor(false).maybe()]); const parameterizedQueryResult = await queryEngine.executeRegisteredQueryAndWait({ - queryId: findFilmActor().hash, + queryId: findFilmActor(false).maybe().hash, params, }); - const regularQuery = findFilmActor({ - actor_id: params['where.actor_id'], - film_id: params['where.film_id'], - language_id: params['include.film.where.language_id'], - last_update: - params['include.film.include.language.where.last_update'], - }); - - const regularQueryResult = - await queryEngine.executeAndWait(regularQuery); + const regularQueryResult = await queryEngine.executeAndWait( + findFilmActor(true).maybe(), + ); expect(parameterizedQueryResult).toEqual(regularQueryResult); }); }); -function findFilmActor(data?: { - actor_id?: number; - film_id?: number; - language_id?: number; - last_update?: string; -}) { +function findFilmActor(regular: boolean) { return from('film_actor') .where({ - actor_id: data?.actor_id ?? param(), - film_id: data?.film_id ?? param(), + actor_id: regular ? params['where.actor_id'] : param(), + film_id: regular ? params['where.film_id'] : param(), }) .include({ film: from('film') .where({ film_id: col('film_actor.film_id'), - language_id: data?.language_id ?? param(), + language_id: regular + ? params['include.film.where.language_id'] + : param(), }) .include({ language: from('language') .where({ language_id: col('film.language_id'), - last_update: data?.last_update ?? param(), + last_update: regular + ? params[ + 'include.film.include.language.where.last_update' + ] + : param(), }) .maybe(), }) .maybe(), - }) - .maybe(); + }); } diff --git a/packages/backend/src/QueryEngine.ts b/packages/backend/src/QueryEngine.ts index e7ab3a7d..598b1e7f 100644 --- a/packages/backend/src/QueryEngine.ts +++ b/packages/backend/src/QueryEngine.ts @@ -235,7 +235,7 @@ export class QueryEngine { const query = queryFn(); - iterateRecursively(query, (x, path) => { + iterateRecursively(query, (x, _) => { if (isQueryParameter(x)) { const value = params?.[x.id]; diff --git a/packages/react/src/createBody.test.ts b/packages/react/src/createBody.test.ts new file mode 100644 index 00000000..58916339 --- /dev/null +++ b/packages/react/src/createBody.test.ts @@ -0,0 +1,79 @@ +import { describe, expect, it } from 'vitest'; +import { col, param, RegisteredQuery, RegularQuery } from '@synthql/queries'; +import { createBody } from './createBody'; +import { from } from './test/generated'; + +const params = { + 'where.actor_id': 2, + 'where.film_id': 47, + 'include.film.where.language_id': 3, + 'include.film.include.language.where.last_update': '2022-02-15 10:02:19+00', +}; + +describe('createBody', () => { + it('Parameterized query returns a RegisteredQueryRequest', async () => { + const request = createBody(findFilmActor(false).maybe()); + + expect(request.type).toEqual(RegisteredQuery); + }); + + it('Regular query without name() returns a RegularQueryRequest', async () => { + const request = createBody(findFilmActor(true).maybe()); + + expect(request.type).toEqual(RegularQuery); + }); + + it('Regular query with name() returns a RegisteredQueryRequest', async () => { + const request = createBody( + findFilmActor(true) + .name('findFilmActorWithRegularQueryWithName') + .maybe(), + ); + + expect(request.type).toEqual(RegisteredQuery); + }); + + it('Regular query with empty string name() returns a RegularQueryRequest', async () => { + const request = createBody(findFilmActor(true).name('').maybe()); + + expect(request.type).toEqual(RegularQuery); + }); +}); + +function findFilmActor(regular: boolean) { + return from('film_actor') + .where({ + actor_id: regular + ? params['where.actor_id'] + : param(params['where.actor_id']), + film_id: regular + ? params['where.film_id'] + : param(params['where.film_id']), + }) + .include({ + film: from('film') + .where({ + film_id: col('film_actor.film_id'), + language_id: regular + ? params['include.film.where.language_id'] + : param(params['include.film.where.language_id']), + }) + .include({ + language: from('language') + .where({ + language_id: col('film.language_id'), + last_update: regular + ? params[ + 'include.film.include.language.where.last_update' + ] + : param( + params[ + 'include.film.include.language.where.last_update' + ], + ), + }) + .maybe(), + }) + .maybe(), + }); +} diff --git a/packages/react/src/createBody.ts b/packages/react/src/createBody.ts index 4bef2621..e967636e 100644 --- a/packages/react/src/createBody.ts +++ b/packages/react/src/createBody.ts @@ -8,7 +8,7 @@ import { export function createBody(query: AnyQuery): QueryRequest { const params: Record = {}; - iterateRecursively(query, (x, path) => { + iterateRecursively(query, (x, _) => { if (isQueryParameter(x)) { if (x.value === undefined) { throw new Error( @@ -27,7 +27,7 @@ export function createBody(query: AnyQuery): QueryRequest { } }); - if (query.name || Object.keys(params).length > 0) { + if (query.name ? query.name.length > 0 : Object.keys(params).length > 0) { if (!query.hash) { throw new Error( [ diff --git a/packages/react/src/useSynthql.ts b/packages/react/src/useSynthql.ts index 841ad942..084ad43b 100644 --- a/packages/react/src/useSynthql.ts +++ b/packages/react/src/useSynthql.ts @@ -26,8 +26,6 @@ export function useSynthql< ): UseQueryResult> { const { endpoint, requestInit } = useSynthqlContext(); - const enrichedEndpoint = `${endpoint}/${query.name ?? query.from}-${query.hash}`; - const mergedRequestInit: RequestInit = { ...requestInit, ...opts.requestInit, @@ -38,6 +36,7 @@ export function useSynthql< body: JSON.stringify(createBody(query)), }; + const enrichedEndpoint = `${endpoint}/${query.name ?? query.from}${query.hash ? '-' + query.hash : ''}`; const queryKey = synthqlQueryKey(query, { endpoint: enrichedEndpoint, requestInit: mergedRequestInit, From d3030dd2be8987a94affda95fb614c1e4fac73ec Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Mon, 23 Sep 2024 07:01:28 +0000 Subject: [PATCH 11/15] small fixes Signed-off-by: Jim Ezesinachi --- packages/react/src/createBody.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/createBody.ts b/packages/react/src/createBody.ts index e967636e..309ffcda 100644 --- a/packages/react/src/createBody.ts +++ b/packages/react/src/createBody.ts @@ -27,7 +27,7 @@ export function createBody(query: AnyQuery): QueryRequest { } }); - if (query.name ? query.name.length > 0 : Object.keys(params).length > 0) { + if (query.name || Object.keys(params).length > 0) { if (!query.hash) { throw new Error( [ From 2cc7f6ab5eda30a6d839eab938463271c53c7a88 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Mon, 23 Sep 2024 19:19:02 +0000 Subject: [PATCH 12/15] added QueryStore, refactor to use and added parameterized query property tests Signed-off-by: Jim Ezesinachi --- packages/backend/src/QueryEngine.ts | 81 ++++------------- packages/backend/src/QueryStore.ts | 89 +++++++++++++++++++ .../arbitraries/arbitraryQuery.ts | 3 + .../arbitraries/arbitraryWhere.ts | 21 +---- .../arbitraries/arbitraryWhereValue.ts | 35 +++++--- .../properties/cardinalityMany.test.ts | 79 ++++++++++------ .../properties/cardinalityMaybe.test.ts | 44 ++++++--- .../properties/cardinalityOne.test.ts | 31 ++++++- .../docs/static/reference/assets/search.js | 2 +- 9 files changed, 249 insertions(+), 136 deletions(-) create mode 100644 packages/backend/src/QueryStore.ts diff --git a/packages/backend/src/QueryEngine.ts b/packages/backend/src/QueryEngine.ts index 598b1e7f..c6d3a586 100644 --- a/packages/backend/src/QueryEngine.ts +++ b/packages/backend/src/QueryEngine.ts @@ -1,12 +1,5 @@ import { Pool } from 'pg'; -import { - AnyQuery, - isQueryParameter, - iterateRecursively, - Query, - QueryResult, - Table, -} from '@synthql/queries'; +import { Query, QueryResult, Table } from '@synthql/queries'; import { composeQuery } from './execution/executors/PgExecutor/composeQuery'; import { QueryPlan, collectLast } from '.'; import { QueryProvider } from './QueryProvider'; @@ -16,6 +9,7 @@ import { QueryProviderExecutor } from './execution/executors/QueryProviderExecut import { PgExecutor } from './execution/executors/PgExecutor'; import { generateLast } from './util/generators/generateLast'; import { SynthqlError } from './SynthqlError'; +import { QueryFunction, QueryStore } from './QueryStore'; export interface QueryEngineProps { /** @@ -89,8 +83,7 @@ export class QueryEngine { private schema: string; private dangerouslyAllowUnregisteredQueries: boolean; private prependSql?: string; - // TODO: fix the callback return type from AnyQuery to TQuery - private queries: Map AnyQuery>; + private queryStore: QueryStore; private executors: Array = []; constructor(config: QueryEngineProps) { @@ -104,7 +97,8 @@ export class QueryEngine { this.dangerouslyAllowUnregisteredQueries = config.dangerouslyAllowUnregisteredQueries ?? false; this.prependSql = config.prependSql; - this.queries = new Map(); + + this.queryStore = new QueryStore(); const qpe = new QueryProviderExecutor(config.providers ?? []); this.executors = [ @@ -155,7 +149,7 @@ export class QueryEngine { }); } - const hasQueryFn = this.queries.has(query.hash); + const hasQueryFn = this.queryStore.has(query.hash); if (!hasQueryFn) { throw SynthqlError.createQueryNotRegisteredError({ @@ -225,43 +219,16 @@ export class QueryEngine { returnLastOnly?: boolean; }, ): AsyncGenerator> { - const queryFn = this.queries.get(queryId); - - if (!queryFn) { - throw SynthqlError.createQueryNotRegisteredError({ - queryId, - }); - } - - const query = queryFn(); - - iterateRecursively(query, (x, _) => { - if (isQueryParameter(x)) { - const value = params?.[x.id]; - - if (value === undefined) { - throw SynthqlError.createMissingValueError({ - params, - paramId: x.id, - }); - } - - x.value = value; - } + const query = this.queryStore.get({ + queryId, + params, }); // TODO: Remove this 'as any', after fixing types - const gen = execute(query as any, { - executors: this.executors, - defaultSchema: opts?.schema ?? this.schema, - prependSql: this.prependSql, + return this.execute(query as any, { + schema: opts?.schema ?? this.schema, + returnLastOnly: true, }); - - if (opts?.returnLastOnly) { - return generateLast(gen); - } - - return gen; } // TODO: fix generic types for input and return types @@ -304,12 +271,10 @@ export class QueryEngine { }); const { params, sql } = sqlBuilder.build(); - const explainQuery: string = `explain (analyze, buffers, verbose, settings, format json) ${sql}`; try { const result = await this.pool.query(explainQuery, params); - return result.rows[0]['QUERY PLAN'][0]; } catch (err) { throw SynthqlError.createSqlExecutionError({ @@ -319,25 +284,11 @@ export class QueryEngine { } } - // TODO: fix the callback return type from AnyQuery to TQuery - // Not sure how to do this yet, or if this is even possible - registerQueries(queryFns: Array<(...params: any[]) => AnyQuery>) { + // TODO: fix the callback return type from AnyQuery + // to some version of TQuery (Query) + registerQueries(queryFns: Array) { for (const queryFn of queryFns) { - const query = queryFn(); - - if (!query.hash) { - throw SynthqlError.createMissingHashError({ - query, - }); - } - - if (this.queries.has(query.hash)) { - throw SynthqlError.createQueryAlreadyRegisteredError({ - queryId: query.hash, - }); - } - - this.queries.set(query.hash, queryFn); + this.queryStore.set(queryFn); } } } diff --git a/packages/backend/src/QueryStore.ts b/packages/backend/src/QueryStore.ts new file mode 100644 index 00000000..458768f4 --- /dev/null +++ b/packages/backend/src/QueryStore.ts @@ -0,0 +1,89 @@ +import { + AnyQuery, + isQueryParameter, + iterateRecursively, +} from '@synthql/queries'; +import { SynthqlError } from './SynthqlError'; + +export type QueryFunction = (...params: unknown[]) => AnyQuery; + +export class QueryStore { + private queries: Map; + + constructor() { + this.queries = new Map(); + } + + /** + * Finds a query from the store and + * applies the parameters to the query. + * + * Throws an error if the query cannot be found, + * or if any of the parameters cannot be applied. + */ + get({ + queryId, + params, + }: { + queryId: string; + params: Record; + }): AnyQuery { + const queryFn = this.queries.get(queryId); + + if (!queryFn) { + throw SynthqlError.createQueryNotRegisteredError({ + queryId, + }); + } + + const query = queryFn(); + + iterateRecursively(query, (x, _) => { + if (isQueryParameter(x)) { + const value = params?.[x.id]; + + if (value === undefined) { + throw SynthqlError.createMissingValueError({ + params, + paramId: x.id, + }); + } + + x.value = value; + } + }); + + return query; + } + + /** + * Checks if a query is in the the store + * and returns a corresponding boolean. + */ + has(queryId: string) { + return this.queries.has(queryId); + } + + /** + * Adds a parameterized query to the store. + * Throws an error if a query with the + * same identifier already exists. + */ + set(queryFn: (...params: unknown[]) => AnyQuery): void { + const query = queryFn(); + + if (!query.hash) { + throw SynthqlError.createMissingHashError({ + query, + }); + } + + if (this.queries.has(query.hash)) { + throw SynthqlError.createQueryAlreadyRegisteredError({ + queryId: query.hash, + }); + } + + this.queries.set(query.hash, queryFn); + } +} diff --git a/packages/backend/src/tests/propertyBased/arbitraries/arbitraryQuery.ts b/packages/backend/src/tests/propertyBased/arbitraries/arbitraryQuery.ts index 31935e7e..05a3ecbb 100644 --- a/packages/backend/src/tests/propertyBased/arbitraries/arbitraryQuery.ts +++ b/packages/backend/src/tests/propertyBased/arbitraries/arbitraryQuery.ts @@ -13,6 +13,7 @@ interface ArbitraryQuery { allTablesRowsMap: AllTablesRowsMap; cardinality: Cardinality; validWhere: boolean; + parameterize: boolean; } export function arbitraryQuery({ @@ -20,6 +21,7 @@ export function arbitraryQuery({ allTablesRowsMap, cardinality, validWhere, + parameterize, }: ArbitraryQuery): fc.Arbitrary { return fc .constantFrom( @@ -36,6 +38,7 @@ export function arbitraryQuery({ allTablesRowsMap, tableName, validWhere, + parameterize, }), limit: arbitraryLimit(), cardinality: arbitraryCardinality(cardinality), diff --git a/packages/backend/src/tests/propertyBased/arbitraries/arbitraryWhere.ts b/packages/backend/src/tests/propertyBased/arbitraries/arbitraryWhere.ts index fc663cad..ae3efdc0 100644 --- a/packages/backend/src/tests/propertyBased/arbitraries/arbitraryWhere.ts +++ b/packages/backend/src/tests/propertyBased/arbitraries/arbitraryWhere.ts @@ -2,7 +2,6 @@ import { fc } from '@fast-check/vitest'; import { AnyDB, Schema, Where } from '@synthql/queries'; import { arbitraryWhereValue } from './arbitraryWhereValue'; import { AllTablesRowsMap } from '../getTableRowsByTableName'; -import { checkIfDateTimeColumn } from '../checkIfDateTimeColumn'; import { getTableWhereableColumns } from '../getTableWhereableColumns'; export function arbitraryWhere({ @@ -10,31 +9,16 @@ export function arbitraryWhere({ allTablesRowsMap, tableName, validWhere, + parameterize, }: { schema: Schema; allTablesRowsMap: AllTablesRowsMap; tableName: string; validWhere: boolean; + parameterize: boolean; }): fc.Arbitrary> { return fc .constantFrom(...getTableWhereableColumns(schema, tableName)) - .filter( - (value) => - // TODO: We should remove this check once we resolve the `date-time` issue - // When there's a mismatch between the timezone of the data stored in the - // database and the timezone of the machine that is running the database, - // the value returned from the database is adjusted to match the timezone - // of the server. But this means when we try to find rows matching the data - // received, we don't get the matching rows returned. So for now, we're using - // the logic below to exempt columns that of the timestampz type from being - // used in this property test - - !checkIfDateTimeColumn({ - schema, - table: tableName, - column: value, - }), - ) .chain((columnName) => { return fc.dictionary( fc.constant(columnName), @@ -44,6 +28,7 @@ export function arbitraryWhere({ tableName, columnName, validWhere, + parameterize, }), { depthIdentifier: '0', diff --git a/packages/backend/src/tests/propertyBased/arbitraries/arbitraryWhereValue.ts b/packages/backend/src/tests/propertyBased/arbitraries/arbitraryWhereValue.ts index 3836ec5c..baf5caa1 100644 --- a/packages/backend/src/tests/propertyBased/arbitraries/arbitraryWhereValue.ts +++ b/packages/backend/src/tests/propertyBased/arbitraries/arbitraryWhereValue.ts @@ -1,6 +1,6 @@ import { fc } from '@fast-check/vitest'; import { AllTablesRowsMap } from '../getTableRowsByTableName'; -import { Schema } from '@synthql/queries'; +import { param, Schema } from '@synthql/queries'; import { getTableDef } from '../getTableDef'; import { getColumnDef } from '../getColumnDef'; import { getColumnPgType } from '../getColumnPgType'; @@ -11,12 +11,14 @@ export function arbitraryWhereValue({ tableName, columnName, validWhere, + parameterize, }: { schema: Schema; allTablesRowsMap: AllTablesRowsMap; tableName: string; columnName: string; validWhere: boolean; + parameterize: boolean; }): fc.Arbitrary { const tableRows = allTablesRowsMap.get(tableName); @@ -30,7 +32,9 @@ export function arbitraryWhereValue({ const columnValuesFromSet = Array.from(new Set(columnValues)); if (validWhere) { - return fc.constantFrom(...columnValuesFromSet); + return fc + .constantFrom(...columnValuesFromSet) + .map((value) => (parameterize ? param(value) : value)); } else { const tableDef = getTableDef(schema, tableName); @@ -52,56 +56,65 @@ export function arbitraryWhereValue({ min: 1, max: 32767, }) - .filter((value) => !columnValuesFromSet.includes(value)); + .filter((value) => !columnValuesFromSet.includes(value)) + .map((value) => (parameterize ? param(value) : value)); } else if (columnPgType === 'pg_catalog.int4') { return fc .integer({ min: 1, max: 2147483647, }) - .filter((value) => !columnValuesFromSet.includes(value)); + .filter((value) => !columnValuesFromSet.includes(value)) + .map((value) => (parameterize ? param(value) : value)); } else if (columnPgType === 'pg_catalog.int8') { return fc .bigInt({ min: 2n, max: 52n, }) - .filter((value) => !columnValuesFromSet.includes(value)); + .filter((value) => !columnValuesFromSet.includes(value)) + .map((value) => (parameterize ? param(value) : value)); } else if (columnPgType === 'pg_catalog.numeric') { return fc .stringMatching(/^[0-9]{0,131072}\.[0-9]{1,16383}$/, { size: 'xsmall', }) - .filter((value) => !columnValuesFromSet.includes(value)); + .filter((value) => !columnValuesFromSet.includes(value)) + .map((value) => (parameterize ? param(value) : value)); } else if (columnPgType === 'pg_catalog.bool') { return fc .boolean() - .filter((value) => !columnValuesFromSet.includes(value)); + .filter((value) => !columnValuesFromSet.includes(value)) + .map((value) => (parameterize ? param(value) : value)); } else if (columnPgType === 'pg_catalog.text') { return fc .string({ minLength: 1, maxLength: 10, }) - .filter((value) => !columnValuesFromSet.includes(value)); + .filter((value) => !columnValuesFromSet.includes(value)) + .map((value) => (parameterize ? param(value) : value)); } else if (columnPgType === 'pg_catalog.tsvector') { return fc .string({ minLength: 1, maxLength: 10, }) - .filter((value) => !columnValuesFromSet.includes(value)); + .filter((value) => !columnValuesFromSet.includes(value)) + .map((value) => (parameterize ? param(value) : value)); } else if (columnPgType === 'pg_catalog.bpchar') { return fc .string({ minLength: 1, maxLength: 19, }) - .filter((value) => !columnValuesFromSet.includes(value)); + .filter((value) => !columnValuesFromSet.includes(value)) + .map((value) => (parameterize ? param(value) : value)); } else if (columnPgType === 'pg_catalog.bytea') { return fc .constant(Buffer.from('pg_catalog.bytea', 'hex')) - .filter((value) => !columnValuesFromSet.includes(value)); + .filter((value) => !columnValuesFromSet.includes(value)) + .map((value) => (parameterize ? param(value) : value)); } else { return fc.constant(undefined); } diff --git a/packages/backend/src/tests/propertyBased/properties/cardinalityMany.test.ts b/packages/backend/src/tests/propertyBased/properties/cardinalityMany.test.ts index ecdd0166..dcf05e2c 100644 --- a/packages/backend/src/tests/propertyBased/properties/cardinalityMany.test.ts +++ b/packages/backend/src/tests/propertyBased/properties/cardinalityMany.test.ts @@ -12,6 +12,15 @@ describe('cardinalityMany', async () => { allTablesRowsMap: await getTableRowsByTableName(pool, schema), cardinality: 'many', validWhere: true, + parameterize: false, + }); + + const validAndParameterizedWhereArbitraryQuery = arbitraryQuery({ + schema, + allTablesRowsMap: await getTableRowsByTableName(pool, schema), + cardinality: 'many', + validWhere: true, + parameterize: true, }); const invalidWhereArbitraryQuery = arbitraryQuery({ @@ -19,47 +28,59 @@ describe('cardinalityMany', async () => { allTablesRowsMap: await getTableRowsByTableName(pool, schema), cardinality: 'many', validWhere: false, + parameterize: false, + }); + + const invalidAndParameterizedWhereArbitraryQuery = arbitraryQuery({ + schema, + allTablesRowsMap: await getTableRowsByTableName(pool, schema), + cardinality: 'many', + validWhere: false, + parameterize: true, }); - it.prop([validWhereArbitraryQuery], { verbose: 2 })( - 'Valid where query should return possibly empty array', - async (query) => { - const typedQuery = query as Query; + it.prop( + [validWhereArbitraryQuery, validAndParameterizedWhereArbitraryQuery], + { verbose: 2 }, + )('Valid where query should return possibly empty array', async (query) => { + const typedQuery = query as Query; - const queryResult = await queryEngine.executeAndWait(typedQuery); + const queryResult = await queryEngine.executeAndWait(typedQuery); - const result = queryResult as any; + const result = queryResult as any; - expect(Array.isArray(result)).toEqual(true); + expect(Array.isArray(result)).toEqual(true); - expect(result.length).toBeLessThanOrEqual(Number(query.limit)); + expect(result.length).toBeLessThanOrEqual(Number(query.limit)); - const expectedKeys = Object.entries(query.select).flatMap( - ([key, selected]) => { - return selected ? [key] : []; - }, - ); + const expectedKeys = Object.entries(query.select).flatMap( + ([key, selected]) => { + return selected ? [key] : []; + }, + ); - for (const item of result) { - const actualKeys = Object.keys(item); + for (const item of result) { + const actualKeys = Object.keys(item); - expect(actualKeys).to.containSubset(expectedKeys); - } - }, - ); + expect(actualKeys).to.containSubset(expectedKeys); + } + }); - it.skip.prop([invalidWhereArbitraryQuery], { verbose: 2 })( - 'Invalid where query should return empty array', - async (query) => { - const typedQuery = query as Query; + it.skip.prop( + [ + invalidWhereArbitraryQuery, + invalidAndParameterizedWhereArbitraryQuery, + ], + { verbose: 2 }, + )('Invalid where query should return empty array', async (query) => { + const typedQuery = query as Query; - const queryResult = await queryEngine.executeAndWait(typedQuery); + const queryResult = await queryEngine.executeAndWait(typedQuery); - const result = queryResult as any; + const result = queryResult as any; - expect(Array.isArray(result)).toEqual(true); + expect(Array.isArray(result)).toEqual(true); - expect(result).toEqual([]); - }, - ); + expect(result).toEqual([]); + }); }); diff --git a/packages/backend/src/tests/propertyBased/properties/cardinalityMaybe.test.ts b/packages/backend/src/tests/propertyBased/properties/cardinalityMaybe.test.ts index ac59bcc2..44bfd014 100644 --- a/packages/backend/src/tests/propertyBased/properties/cardinalityMaybe.test.ts +++ b/packages/backend/src/tests/propertyBased/properties/cardinalityMaybe.test.ts @@ -12,6 +12,15 @@ describe('cardinalityMaybe', async () => { allTablesRowsMap: await getTableRowsByTableName(pool, schema), cardinality: 'maybe', validWhere: true, + parameterize: false, + }); + + const validAndParameterizedWhereArbitraryQuery = arbitraryQuery({ + schema, + allTablesRowsMap: await getTableRowsByTableName(pool, schema), + cardinality: 'maybe', + validWhere: true, + parameterize: true, }); const invalidWhereArbitraryQuery = arbitraryQuery({ @@ -19,9 +28,21 @@ describe('cardinalityMaybe', async () => { allTablesRowsMap: await getTableRowsByTableName(pool, schema), cardinality: 'maybe', validWhere: false, + parameterize: false, }); - it.prop([validWhereArbitraryQuery], { verbose: 2 })( + const invalidAndParameterizedWhereArbitraryQuery = arbitraryQuery({ + schema, + allTablesRowsMap: await getTableRowsByTableName(pool, schema), + cardinality: 'maybe', + validWhere: false, + parameterize: true, + }); + + it.prop( + [validWhereArbitraryQuery, validAndParameterizedWhereArbitraryQuery], + { verbose: 2 }, + )( 'Valid where query should return a possibly null, non-array, TS object result', async (query) => { const typedQuery = query as Query; @@ -46,16 +67,19 @@ describe('cardinalityMaybe', async () => { }, ); - it.skip.prop([invalidWhereArbitraryQuery], { verbose: 2 })( - 'Invalid where query should return null', - async (query) => { - const typedQuery = query as Query; + it.skip.prop( + [ + invalidWhereArbitraryQuery, + invalidAndParameterizedWhereArbitraryQuery, + ], + { verbose: 2 }, + )('Invalid where query should return null', async (query) => { + const typedQuery = query as Query; - const queryResult = await queryEngine.executeAndWait(typedQuery); + const queryResult = await queryEngine.executeAndWait(typedQuery); - const result = queryResult as any; + const result = queryResult as any; - expect(result).toEqual(null); - }, - ); + expect(result).toEqual(null); + }); }); diff --git a/packages/backend/src/tests/propertyBased/properties/cardinalityOne.test.ts b/packages/backend/src/tests/propertyBased/properties/cardinalityOne.test.ts index 867a8160..15fb7b0f 100644 --- a/packages/backend/src/tests/propertyBased/properties/cardinalityOne.test.ts +++ b/packages/backend/src/tests/propertyBased/properties/cardinalityOne.test.ts @@ -13,6 +13,15 @@ describe('cardinalityOne', async () => { allTablesRowsMap: await getTableRowsByTableName(pool, schema), cardinality: 'one', validWhere: true, + parameterize: false, + }); + + const validAndParameterizedWhereArbitraryQuery = arbitraryQuery({ + schema, + allTablesRowsMap: await getTableRowsByTableName(pool, schema), + cardinality: 'one', + validWhere: true, + parameterize: true, }); const invalidWhereArbitraryQuery = arbitraryQuery({ @@ -20,9 +29,21 @@ describe('cardinalityOne', async () => { allTablesRowsMap: await getTableRowsByTableName(pool, schema), cardinality: 'one', validWhere: false, + parameterize: false, + }); + + const invalidAndParameterizedWhereArbitraryQuery = arbitraryQuery({ + schema, + allTablesRowsMap: await getTableRowsByTableName(pool, schema), + cardinality: 'one', + validWhere: false, + parameterize: true, }); - it.prop([validWhereArbitraryQuery], { verbose: 2 })( + it.prop( + [validWhereArbitraryQuery, validAndParameterizedWhereArbitraryQuery], + { verbose: 2 }, + )( 'Valid where query should return a non-null, non-array, TS object result', async (query) => { const typedQuery = query as Query; @@ -49,7 +70,13 @@ describe('cardinalityOne', async () => { }, ); - it.skip.prop([invalidWhereArbitraryQuery], { verbose: 2 })( + it.skip.prop( + [ + invalidWhereArbitraryQuery, + invalidAndParameterizedWhereArbitraryQuery, + ], + { verbose: 2 }, + )( 'Invalid where query should throw expected cardinality error', async (query) => { try { diff --git a/packages/docs/static/reference/assets/search.js b/packages/docs/static/reference/assets/search.js index d9cb74ba..fea4ef91 100644 --- a/packages/docs/static/reference/assets/search.js +++ b/packages/docs/static/reference/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file From d6eada068a74064f3d73a943e28e02629a19a721 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Mon, 23 Sep 2024 20:11:32 +0000 Subject: [PATCH 13/15] fixes Signed-off-by: Jim Ezesinachi --- packages/backend/src/QueryEngine.ts | 2 +- packages/backend/src/QueryStore.ts | 34 +++--- packages/backend/src/SynthqlError.ts | 104 +++++++++--------- .../arbitraries/arbitraryWhereValue.ts | 27 +++-- .../docs/static/reference/assets/search.js | 2 +- 5 files changed, 91 insertions(+), 78 deletions(-) diff --git a/packages/backend/src/QueryEngine.ts b/packages/backend/src/QueryEngine.ts index c6d3a586..ddbaf9c0 100644 --- a/packages/backend/src/QueryEngine.ts +++ b/packages/backend/src/QueryEngine.ts @@ -144,7 +144,7 @@ export class QueryEngine { ): AsyncGenerator> { if (!this.dangerouslyAllowUnregisteredQueries) { if (!query.hash) { - throw SynthqlError.createMissingHashError({ + throw SynthqlError.createQueryMissingHashError({ query, }); } diff --git a/packages/backend/src/QueryStore.ts b/packages/backend/src/QueryStore.ts index 458768f4..8caab91e 100644 --- a/packages/backend/src/QueryStore.ts +++ b/packages/backend/src/QueryStore.ts @@ -31,25 +31,31 @@ export class QueryStore { const queryFn = this.queries.get(queryId); if (!queryFn) { - throw SynthqlError.createQueryNotRegisteredError({ - queryId, - }); + throw SynthqlError.createQueryNotRegisteredError({ queryId }); } const query = queryFn(); + // Check if all required parameters are provided + const missingParams: string[] = []; + iterateRecursively(query, (x, _) => { - if (isQueryParameter(x)) { - const value = params?.[x.id]; + if (isQueryParameter(x) && params[x.id] === undefined) { + missingParams.push(x.id); + } + }); - if (value === undefined) { - throw SynthqlError.createMissingValueError({ - params, - paramId: x.id, - }); - } + if (missingParams.length > 0) { + throw SynthqlError.createQueryParameterMissingValueError({ + params, + paramIds: missingParams, + }); + } - x.value = value; + // Apply parameters + iterateRecursively(query, (x, _) => { + if (isQueryParameter(x)) { + x.value = params[x.id]; } }); @@ -69,11 +75,11 @@ export class QueryStore { * Throws an error if a query with the * same identifier already exists. */ - set(queryFn: (...params: unknown[]) => AnyQuery): void { + set(queryFn: QueryFunction): void { const query = queryFn(); if (!query.hash) { - throw SynthqlError.createMissingHashError({ + throw SynthqlError.createQueryMissingHashError({ query, }); } diff --git a/packages/backend/src/SynthqlError.ts b/packages/backend/src/SynthqlError.ts index 38c6b600..eb0dded8 100644 --- a/packages/backend/src/SynthqlError.ts +++ b/packages/backend/src/SynthqlError.ts @@ -44,10 +44,10 @@ export class SynthqlError extends Error { const lines = [ 'Database connection error!', '', - 'Failure to establish a connection to your database.', + 'Failure to establish a connection to your database', '', 'Check your connection string, and make sure your', - 'database is up and can accept new connections.', + 'database is up and can accept new connections', '', 'Here is the underlying error message:', '', @@ -80,62 +80,55 @@ export class SynthqlError extends Error { return new SynthqlError(error, type, lines.join('\n')); } - static createMissingHashError({ query }: { query: AnyQuery }) { - const type = 'MissingHashError'; + static createPrependSqlExecutionError({ + error, + prependSql, + }: { + error: any; + prependSql: string; + }): SynthqlError { + const type = 'PrependSqlExecutionError'; - const lines = [ - 'Missing hash error!', - '', - 'The query:', - '', - JSON.stringify(query, null, 2), + const lines: string[] = [ + '# Error executing prepended SQL query', '', - 'is missing its `hash` property, which is', - 'used as the key when registering it', - 'via QueryEngine.registerQueries()', + printError(error), '', + 'This error was caused by the following prepended SQL query:', + tryFormatSql(prependSql), ]; - return new SynthqlError(new Error(), type, lines.join('\n')); + return new SynthqlError(error, type, lines.join('\n')); } - static createMissingValueError({ - params, - paramId, - }: { - params: Record; - paramId: string; - }) { - const type = 'MissingValueError'; + static createQueryAlreadyRegisteredError({ queryId }: { queryId: string }) { + const type = 'QueryAlreadyRegisteredError'; const lines = [ - 'Missing value error!', - '', - 'No value found for the parameter:', - '', - JSON.stringify(paramId, null, 2), + 'Query already registered!', '', - 'in the `params` object:', - '', - JSON.stringify(params, null, 2), + 'A query already exists in the query store for the queryId:', '', - 'Check and make sure the correct value', - 'is included in the `params` object', + JSON.stringify(queryId, null, 2), '', ]; return new SynthqlError(new Error(), type, lines.join('\n')); } - static createQueryAlreadyRegisteredError({ queryId }: { queryId: string }) { - const type = 'QueryAlreadyRegisteredError'; + static createQueryMissingHashError({ query }: { query: AnyQuery }) { + const type = 'QueryMissingHashError'; const lines = [ - 'Query already registered error!', + 'Query missing hash!', '', - 'A query already exists in the query store for the queryId:', + 'The query:', '', - JSON.stringify(queryId, null, 2), + JSON.stringify(query, null, 2), + '', + 'is missing its `hash` (i.e `query.hash`) property,', + ' which is used as the key when registering it', + 'via QueryEngine.registerQueries()', '', ]; @@ -146,39 +139,46 @@ export class SynthqlError extends Error { const type = 'QueryNotRegisteredError'; const lines = [ - 'Query not registered error!', + 'Query not registered!', '', 'No query found in the query store for the queryId:', '', JSON.stringify(queryId, null, 2), '', 'Check and make sure the correct queryId', - '(i.e query.hash) is being passed', + '(i.e `query.hash`) is being passed', '', ]; return new SynthqlError(new Error(), type, lines.join('\n')); } - static createPrependSqlExecutionError({ - error, - prependSql, + static createQueryParameterMissingValueError({ + params, + paramIds, }: { - error: any; - prependSql: string; - }): SynthqlError { - const type = 'PrependSqlExecutionError'; + params: Record; + paramIds: string[]; + }) { + const type = 'QueryParameterMissingValueError'; - const lines: string[] = [ - '# Error executing prepended SQL query', + const lines = [ + 'Query parameter missing value!', '', - printError(error), + 'No value found for the parameter(s):', + '', + JSON.stringify(paramIds, null, 2), + '', + 'in the `params` object:', + '', + JSON.stringify(params, null, 2), + '', + 'Check and make sure the correct values for each', + 'parameter is included in the `params` object', '', - 'This error was caused by the following prepended SQL query:', - tryFormatSql(prependSql), ]; - return new SynthqlError(error, type, lines.join('\n')); + return new SynthqlError(new Error(), type, lines.join('\n')); } static createResponseStreamingError({ diff --git a/packages/backend/src/tests/propertyBased/arbitraries/arbitraryWhereValue.ts b/packages/backend/src/tests/propertyBased/arbitraries/arbitraryWhereValue.ts index baf5caa1..7eda3704 100644 --- a/packages/backend/src/tests/propertyBased/arbitraries/arbitraryWhereValue.ts +++ b/packages/backend/src/tests/propertyBased/arbitraries/arbitraryWhereValue.ts @@ -34,7 +34,7 @@ export function arbitraryWhereValue({ if (validWhere) { return fc .constantFrom(...columnValuesFromSet) - .map((value) => (parameterize ? param(value) : value)); + .map((value) => parameterizeValue(parameterize, value)); } else { const tableDef = getTableDef(schema, tableName); @@ -57,7 +57,7 @@ export function arbitraryWhereValue({ max: 32767, }) .filter((value) => !columnValuesFromSet.includes(value)) - .map((value) => (parameterize ? param(value) : value)); + .map((value) => parameterizeValue(parameterize, value)); } else if (columnPgType === 'pg_catalog.int4') { return fc .integer({ @@ -65,7 +65,7 @@ export function arbitraryWhereValue({ max: 2147483647, }) .filter((value) => !columnValuesFromSet.includes(value)) - .map((value) => (parameterize ? param(value) : value)); + .map((value) => parameterizeValue(parameterize, value)); } else if (columnPgType === 'pg_catalog.int8') { return fc .bigInt({ @@ -73,19 +73,19 @@ export function arbitraryWhereValue({ max: 52n, }) .filter((value) => !columnValuesFromSet.includes(value)) - .map((value) => (parameterize ? param(value) : value)); + .map((value) => parameterizeValue(parameterize, value)); } else if (columnPgType === 'pg_catalog.numeric') { return fc .stringMatching(/^[0-9]{0,131072}\.[0-9]{1,16383}$/, { size: 'xsmall', }) .filter((value) => !columnValuesFromSet.includes(value)) - .map((value) => (parameterize ? param(value) : value)); + .map((value) => parameterizeValue(parameterize, value)); } else if (columnPgType === 'pg_catalog.bool') { return fc .boolean() .filter((value) => !columnValuesFromSet.includes(value)) - .map((value) => (parameterize ? param(value) : value)); + .map((value) => parameterizeValue(parameterize, value)); } else if (columnPgType === 'pg_catalog.text') { return fc .string({ @@ -93,7 +93,7 @@ export function arbitraryWhereValue({ maxLength: 10, }) .filter((value) => !columnValuesFromSet.includes(value)) - .map((value) => (parameterize ? param(value) : value)); + .map((value) => parameterizeValue(parameterize, value)); } else if (columnPgType === 'pg_catalog.tsvector') { return fc .string({ @@ -101,7 +101,7 @@ export function arbitraryWhereValue({ maxLength: 10, }) .filter((value) => !columnValuesFromSet.includes(value)) - .map((value) => (parameterize ? param(value) : value)); + .map((value) => parameterizeValue(parameterize, value)); } else if (columnPgType === 'pg_catalog.bpchar') { return fc .string({ @@ -109,12 +109,12 @@ export function arbitraryWhereValue({ maxLength: 19, }) .filter((value) => !columnValuesFromSet.includes(value)) - .map((value) => (parameterize ? param(value) : value)); + .map((value) => parameterizeValue(parameterize, value)); } else if (columnPgType === 'pg_catalog.bytea') { return fc .constant(Buffer.from('pg_catalog.bytea', 'hex')) .filter((value) => !columnValuesFromSet.includes(value)) - .map((value) => (parameterize ? param(value) : value)); + .map((value) => parameterizeValue(parameterize, value)); } else { return fc.constant(undefined); } @@ -123,3 +123,10 @@ export function arbitraryWhereValue({ return fc.constant(undefined); } } + +function parameterizeValue( + parameterize: boolean, + value: T, +): T | ReturnType { + return parameterize ? param(value) : value; +} diff --git a/packages/docs/static/reference/assets/search.js b/packages/docs/static/reference/assets/search.js index fea4ef91..af4f903d 100644 --- a/packages/docs/static/reference/assets/search.js +++ b/packages/docs/static/reference/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,H4sIAAAAAAAAE71dW5Pcto7+K1s9eWx3RN01b7GTquTsbuJje8+prSnXlNzNaSvRSG1JbXvW5f++ReoGsAHdWpOX2HETwEd8BEhClPhtU+Rfys3t3bfNX0l22Nza200WP8rN7eZDvP9LZocfy2K/2W7ORbq53Tzmh3Mqyx+b3+7LYr/7WD2mm+1mn8ZlKcvN7WbzfXupbZ8mpKZ9mszQ8jHODqksXsivp0KWJamxaXPftFmgPZNfq0HVqsEMvUlWFXl5kntaa//zDJ2fzrJIJO2B5rcZ2goZM+D0L6OafLcnOk9Tua/+Ky6rTtnDOdtXSZ7hgQNaEsq3m1NcyKwyRiJj8/GUl/KfZ1k8jRrtmy6y6nmO3xm+v6+eTnKuyRv0L52OURhYE+p0D1BYdu+Y8lP68pykB1msgXGH1F0Bt+0zgzo+Hx9lVsnDIkJJ5BcqV0Vve/2Q0E1ep3HW4U6yShYP8d7Im13DJcMQ+euXr3J/Vp75j3fJo5xn96YTvm+Exz3Td5EBpH7Lkuy4BE8ruyKct7KqkuxYzkQCxJaBGMwV8zDMyRK9iq4HjGPeFcnxKIu5jgFiKw6WheNkPRALACw2fpExfsmOSSZfF/lpGhug/dX5Q1lbYvOmFpzoAdhDbrraf5SP8TIsnex6cA5xdpRFfi7Tp5/SNP/yP1khj0lZyaKeRxK5jKubaYrX68ipkCeZHd5+Wsgzkl8TVv45OUzNPwSqXnxFUHm+1Eu15HpQ0vx4nJoYL9D0wtcBskMqUXWYGuUcoCW5yRNg25hnZVWc91VezDJ5gwVnuWDSyJgE4mJEVOXhRVK+OBXJ57iSV2AxMuUkNESGXA3PnFQ5CezcFLlaT4hcOY1rOkeuhktt6p/eVnkxL/ZukNwz4JJ6/5AXM/mFYuugsi03RBWBJJ3pql5oab4YXPHPwbBr/nwhZq/8G22GooEKwToAd+X81YEJcqQucIqL+HHmOGPhdspWRoyGYT3KZw6BXmjpMKQw/JQd/h0n1SIoveyaiN6ghP60CNmljmdEeI0LWVUr4T2lcZLNRdYKrYKhnZ8XTfeXwouXbGDB+vYpqz5+Sn8pipHlI2y4ZMmK555CxpV8FReHJIvTpHpaYP+GVTLuF9TrQYw/x1X8IS7lqzzLpK6tLofK61oT8T/KPHsdF2WSHZdDJZSsifG/k1Kp/jUuPy7HSCh5Boz/itOzvBok0rImyjpVpoWMD099Al2Od1jf6sh/z6vVUJO61kT8utu/dE8ElkMeULYm5jeyPOVZKd9WhYwfr8oJrKo18a7j3FW9uqTYQgCbWW1hwOCNZTEXRS2xjvXRndul+cnbtCn2H2VZxse5EHqpiz11kn2URVLJwzWo9vlhLqRGZLFX4GN+Pfx/qc91NM1/rY9kEA+JqXMgAxqGl33UwRMY6lEgvD6QfpdfKwafGiTEQZJLiWl4uqMqE2sQ88zfjNQeTBSEnlleeiM/nSU4NzIZbSO43GfmIBtg8HKEdcA42TVwHWUmi7iiDpsYB4jalsNWjUNJ3Fkewhw8XrTP02Ez8JwSaUPXYUas6DZX2vnEnCWBdnSbF2KRpWnngjhrdZl2rNYHTNfS9X+5RcdDkY95lsWwa4RnAaGqYTjmcaWnjm+IZ8K5rAX+H7IzluNgVwfPWiFvT7A43cNTzkyVUp2lm2W/E1kHwZePspjl8l0rsY79JNun58M8BL3MOhjS5DGZR0IrsY79/OGhlPMAdCLrINj3FatZMLDcSmzE/zcPRCOwjvVjkZ9PL+cB6GXWwfAxLj/OAtAIrGNd/zHHeiOw2Lo52/yUPb2LP6TyZ/kwgAO0+htmHtPanPkH9ofdoaXnx6ycD2DXS84CMomFn18OA/r55RLPc3RP6PxK5l5pnw3bq9usZHBs/dQ2WWLO6S29/O33n9787/0fr39589O7P9687Sx+jotE+Q9bNZuv0dmXSRYXT3+c1FYmN/fPyDhquKbpCUbXMPdq0pQJWq1idGzkrjdsa01v5IMsZLYfik6j5XrG9ROKUcO61XpG3w1PBH2jNUz+Nrr6bVqsYewfeZINhkfd4Mok1NRQXqvNv6xABYZOQmbzNTpaH92+AMCsZa4yvWQDiw3O3sn2HZtSBJ+DYbcQyMiC8vNIGA8AakVXRpQclsHRctdjAdHyRh7PaVzgBQIdKbDplTHKneFhDcPWiyIUvDgB+2GWjsFZaa7rk4rGNAo+SmbaHX5oBIcC1dmho6KLEbXS10IymILETyeLkPq7+OJMz6KM6vUQa78drkHWq1gbnHH0cgG2kfOWs6CR0zT98OgiH18xihiz5TmdYFW1WsPoG/kwuPTSv/8NC5Dezpx1R42eGWQ/FIN1mguLu/taYKLZy+nz2i5rAI3aF2I2kp3uMJelRioZI2B2rfwCSGMl3rGt4xi0TsH62PJpoUHj0sKrYIJz31v2pUa0e9Gtrp7bfuBfoWSs3dyPvDkJu9705YqJFVqeETbDhg+y3BfJST3enGMfiK0C41TkJ1lUzGuhHIpeahUQhfx0Tgo5uo6AEFqZVQDEh0OiXBqnrxf5g5JfBdgPB/kwC8l9I7DA8NRX/wdNzzkO0AjXfXweHLv7+yQ7yK/zJjwIzFA0YVN9LcSpG+0BlCMzzqoenV43WQ54YbIcxo2VPif8ZUl2GD3S+ZzglyTnYehA43MCf8iLx3h0wz4ddqfvOUFfOxMNd4HR/qy5JqnSNZNNo+5ZIZfv1k2Qrb7nBP2YZMnj+XE91L3CZ4Udf10ZdqfwOWHLbE3MjbaVAcMN3cWREgbzNYdKZi8x8JGOycuIseMkMxYLGMHcBcEYkOnTPsYxc2ofgzF1AscgZk3SYxCWzGoYzuKZi4AGA6N+ov3zhF1W3/Lq4DAPPE2zeDPntBPo13U7KwbEnH0eUNF24YqsMQxo+m7kEtVqq/gRiDPjezbQqQE/AnNWBpgNcklKGAG8OEdMAU8mjcmA/5b5FFubPqH23bl+RjUwzJ1SR6EsCMJFk+ookNlBtmBaHQVxVRBdObFS4MggWQCtF/l7A8ewuyCEQGcH33pBT8rmwkIqVgVXv1xyFTikYlVw+t2bq7BBDatCy85pehUyoGBdQsvXRfIYF0//KUeP1/CUYiXXAjSPB7wdew+sbrDGSYSxs/arHbT/98ibZfr31Qy9SuNzOWqubnXlO6lJyRwwpV/WNJtfbR2fJeGMLj9Rgm0NHgHjja91BsxEwx4dHICyxtlBgEO9bYXPTdK2u3ZX2jv8+cH+Fb4SRptrm13r5Uq/hv5G7s9FmXyW6Vg3LwWuRHAuZXMqnLDc32zRNxu219+SQVorax2aKDhDUCaNtmPvnI9YTtpvWUyyfdF6vu2LWYcxXyfP3vTVhkGn2/P+zVegB7tstL3Kbj9aXuVZJb9SiYMaW03rBc6Gh4loy2ARctHpxXaNIv3hlCfZfJM3QHLEtiE+tFuUZfVblixAg4UXAwJH4Euakv4E/EXUN01nBP377aY+MXH7bfNZFqUqAdxu7J2zizbbzUMi04O6xKl943afP6rbTjbvm9/+Jff6U7u3d3WTH63N9s7autFOhO/fb+9aAf3v+h90K7HZ3gmilUCt7M32zt46Yud43tbZOt7Osh3U3kbtHdjepdo7qL272d55BAoXtfI22zufaOWhVv5mexcQrXzUKths70KiVYBahZvtXUS0ClGrSPnQ2trBzrJ91C7CvlauF6S3DVI0KzbVEPMilNuFQzXEhAjlb9EysRVeQw6WwaQI5f2u5Vb4pAymSCguBOV+gVkSPus0gZkSihJBcSUwWUKxIvyt6+4sO8ItMWFCM0YRKzBjtuLFpqLIxozZihebotY2IkmHkr11wp2wjBDClNk6iByqOzYmylYc2C7ZEtNjKxJsKtJsTI+tOLBJX9qYHluRYFOM25geW3FgUzzamB1bcWBHpG1Mj2OxKB3MjyNYtzuYIMdm3e4YSc5h3e5gghzFgkMNIwfz4ygWHGoYOZgfR5HgUBnCwfQ4ARtnDubHUSw4VC5xMD+OIsGh+435cRUJDpnYMT2u4sChcruL2XEVBQ412lxMjqsYcKjR5hpTj+aGygUu5sZVDLgUiS7mxlUMuBSJLubGVQS4FIkupsZVBLgUNS6mxlX+d12qIWbGU/53KWY8zIyn/O+Ssy5mxlP+dylmPMyMp/zvUsx4mBlP+d+lmPGMdYHyv0cx42FmPOV/j2LGw8x4AZuCPEyNpwjwKA49TI2nCPCcrR3tPByHHqbGVwR4FIc+psZXBHgUhz6mxlcEeBSHPqbGVwR45OIJU+O7bFbxMTe+5oZi2zeWbZobim0fc+MrAnyKbR9T4ysCfIptH1PjKwJ8ikMfUxMoAnxn61o73wnwuhFzEwjWQQEmJ1AU+GQ2DTA7gcNbx/QEPD0BpifweOuYn0Cx4HtkS2NprQkiZ+UAMxRohgKyJaYo0BSRC3ZMUagpItfsmKFQ0RBYlO0QMxQqGgJqHIWYoFCxEFDjKMT8hIqEgMrnIaYnVBwEVC4IMTuh3vVQuSDE5IT8eiA09j6KgYCkMcTkhIqCgEobISYnUhQEFIsRJifS5FAsRpibSDEQUskgwtxEioGQIjHC3ESKgZAiMcLcRIqBkCIxwtxEPjvSIkxOpHelZDRGmJxIMRBSfEfG3lQxEFK5PzI3p4qCkNy6Wcb21BJs3Na/wbaaIXL7Zhl7VEtzRG7LLGNraikuIor3+ifY1ON3mpaxKbV8bpKuf4JNFSERvZk39qWWLiPYtLuMnamlWInIHb1lEKaLBhGVHMRFPUGRElHDRZgVBV04oF1g1hQEPyEJs5ag6wQRNQ6FWULQlQIGgcGXLhVE5Jg1qwi6WEDvWoRZSNDlgogcs2YlQRcMInLMGrUEoUsGwiIHrVFOEDa/fhC2WQPSRSCLHIpGTUHoygGn16DMrlcR9Lg1CgtC1w/o1CWM2oKw69oPXbYyaKvLC+TGXRgFBlFXGJi2Bm82uxwXRpFBODVvLqnXqDMIpybO2zr+zjL6ZlQahFMT55NtzfpdTVxAtjWIc2riyPFrFByEM5AdjZqDcAZqdkbZQejiApPzjMKD0PUFmguj9CCcaIgLgzjX4rkwChDCFTwXRg1CuDVvZNAbZQjhDvDmmvVWzZsgE4RRjBC65iDoYrJRjxC67CDoerJRkhC68iDokrJRlRC6+CAEOQMZhQmh6w9CkFOQUZsQugRBjwejOiE8MTAejAqF8Gx+PBhFCuE5/Hgw6hTCc3mOPbM+XvNGToRGtULoogS9AhdGwUJ4/LpeGCUL4fHxZhQthBcNuMygzbd4lxmlC+EL3mVG9ULoIoWgHyoYBQxRVzDIrhklDNHUMOihY1QxhO/xfvDNJxv+gB8M2vxgwA8GbX4dbmRaNyoawuenN6OmIYL6qRSZzYyqhqjLGuQDEWHUNYSuXgjymYgwKhtC1y+ETWYoo7Yh6uKGTWYoo7oh6vIGWbQQRn1DBP7Acicwn0nxNQ5hFDmELmUIm0yTRplD6GqGIJ/QCKPSIXRBQ9hkKjGKHSKsiSNjyCh3iLAmjhxnRsVDhDVx5OAxih5C1zYE+RhEGHUPocsbgnwSIozSh9AVDkE+DBFG9UPU5Q+HfLwjjAKI0GUO4ZDEGSUQoSsdrGKDOV3tEOSDEWFUQoQueAjy2YgwiiEiqh8H0xtzoyAidN1DkI9IhFETEVFNHf3E1KAu8oZA1Nzpsw6fZVHJw2/1mYe7u+79qm+b5tMRt8JqT2B820Sb22/ftxvh1386Qf2n79Z/BqL5s/n/sPldbfHrv9ii/YvT/MVpf3IapWqmvP32/Xt/ukL9n+pAf+ofvhcBkQY9VLUgr/W57V88t4XfovBZU9lT+/GkXn8QAU8IXvLwAUkBUGHES32qzyf2giF0vM0LNmfqgcUQCFojggf1QhCQ9QBan5M9H9X/y8MF5v6Ss28bwbkIXu0JZEF3OdAf9Neg7/PmG9SI/VCAXjuDCtT3p4CgAwS9EcH2K9lAHHQ5chlxdOsC8Dboc9gMyojz+j5NTJeBLnPOrq8F60U8QLDPC6VQxgd2/JCXUQfe01id8AXu6UW5YLuMtNAFPm2TiM1xU8sbA1mtVXvn+Fz8dLJMShEh1DLQ91qLIewDYW8YftF/thv4AZAVDRtHlgNgOAzb9McOLK3AzPsh9J/FRWQt+7n+6C8QBgAiLul118D3cg4IJofH+3jKS3mRfEB65n0F7k/sJW0gajeTHDsN1ZePgYiW9d2QvToX9MIdVnNobvPdd7f5XmoD+cllO6a1NbfmNccfP7YXqoHoB7zwMaF1/Vnm2am+vPcSEohPl6VXq3msr65VJ+sv1YDh7bEjDKrRA+1SD+iVx+Y0rUfd/TbgHjAOfHb4aUWn+ubV8lMq2ztCL3EBdR47NWh1ejDH9Y25RfcqxqVGEJTeMH9aY5ZXQ9pAcvO4KbPWVjTXtpbtta0Xyjwwl/G5TisbdJoHJhs2ZR1ihSA/l+lTnKb5l3PWd7N5WwHFNwihduXpcO67SOMWnAMcjkf01jCUdqG01a5226W0161FWcV/frDr64KAVgd4W23caNH+SDgUhd1hV0r1513gZAb7weYhfTUsIBNkQo+V0WMBzwMgM7hcZmjk4uzwJU5QHx2QEFwuITTieOCgCcUB0eZyw4VWQ4ECfndZt7dxgXyPhtCgpLEodkAwOVxQyq+nNE6QRQdkLpcdXeCOVigLYo2RbL9KBsMkgsOLFdRX+YGVKfBMYNeRFHD97G/2BApAHLG7QX1b2Ae8cgcDO+QoIWYY23DOdsML45APgF/ZgFdCxKYM+pZFq77gCBZTcCdntWtIh3MRfIEaWoZpKuAisbsdEPACQi9orVu8AnTHKkxAYBxzwuWpflH4L2m4zYHgeXHt8FP/bilU4UEV3EopKQv5gDelqqQIOONSoJJEeado37GEquBKPmR9qFSp9y55PXDiCLkQTdrlp9Zj+tSBqSzkZoTmBcUCvtEIlcB9b8iF+595khk7fdgBi+tAfTkhCDxgje11c70kGMBw48sRn+bHY5IdUYIAZNlctHXfi4PzMxyt7K6ju7gchAigxOcGWvdhPWgRpgiXW6rpFtCdwBzLXXdFM1ytgqlsQGxgdQ9c609XQYSCB8j12T50X0eAMQR9FnAg2rtCwXCCm1uOJCOFWHBE2Bw/zb3QwBSA6HMjt71lAkz7kJ92dSu4cXhKY7zEgVMUazSNs8yIF1RjaQu8XF455biwBZOs3Ra1Oe/2Oz6kAnTbbuw7HK9c3RrOEk67IXDbvYJnt3/p6u/cmDs1L97i3Q9QzxbR4DvecDCA9fuAJEqYPhh3QdOJoC3Cs+tXrUZmxyRDEQMnHZvjFcgqF+PegyWIzXGrFRgLIDhrCi56+Lk/gksfi1s91PJmMKDAHZIkclMEJzk2Ehrp+koVIAzDkI1DLVxWeYH3bKDD7C65f9cWTgkg4bByuLprwSWAzfnoclllQVbY4TCwJ4zgAkZMVEDRBPOW4N1V6yFqGi7oicvFJlzSIetwhFkczyMLwgimLfZpTyOYZIlRhkAZn3dA++kxSCL0ndNmR7dNnF6ba/w2g7K1vPZKDpAu4A6t3aJ1Ty27ZxFcXLV3vYNsCLwUtM842T0q/EoWXGpBZwXcqCtlVSXZEU8uMI2xVvGc5oDR4bA9/ZR+OCfpwdjxwLhkcdbrq337Jj8cFRBu1FLLPpZrNF1WNgEMdmXaCJPZO4QOt7g5q9XQfRoD9gRGB/sYdnCvBIO0e5bOPkO+HDUWXGy2ixMRcP6gnger04JgfcXaTvAiH0l1D+JZ4coc7jBLs/WnqkiOR2Otg57XcYvPqrw45IDGLZtOTTEPMNQtzqw2yEU7fEV3/KFt43QP5bpFXvsvfpe9OGer717AjAWg21wWPpeybL/QA4cYHCHs8/5emAxZmI3ZcxEXjwgjlCk4qr7UnyQD6RRYC9qYCAbFLzdh0HTALR607L75SBmUhiuAgEyP77ebU3KSqVrL3t69//79/wGE0SeB/60AAA=="; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file From a41c1e0fe070ae822ae3e4b7dbde4a2b3cb64c30 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Thu, 26 Sep 2024 06:40:35 +0000 Subject: [PATCH 14/15] fixes Signed-off-by: Jim Ezesinachi --- packages/backend/src/QueryEngine.test.ts | 2 +- packages/backend/src/QueryEngine.ts | 37 ++++++------- packages/backend/src/QueryStore.ts | 16 ++---- .../executors/PgExecutor/queryBuilder/exp.ts | 2 +- packages/docs/docs/200-security.md | 2 +- .../static/reference/assets/navigation.js | 2 +- .../docs/static/reference/assets/search.js | 2 +- .../src/createExpressSynthqlHandler.ts | 54 +++++++++---------- .../src/createNextSynthqlHandler.ts | 54 +++++++++---------- packages/queries/src/index.ts | 4 +- packages/queries/src/types/QueryRequest.ts | 9 +++- .../queries/src/util/iterateRecursively.ts | 30 +++-------- ...edQuery.ts => isRegisteredQueryRequest.ts} | 0 ...gularQuery.ts => isRegularQueryRequest.ts} | 0 14 files changed, 90 insertions(+), 124 deletions(-) rename packages/queries/src/validators/{isRegisteredQuery.ts => isRegisteredQueryRequest.ts} (100%) rename packages/queries/src/validators/{isRegularQuery.ts => isRegularQueryRequest.ts} (100%) diff --git a/packages/backend/src/QueryEngine.test.ts b/packages/backend/src/QueryEngine.test.ts index 7fc34b79..e06dbe54 100644 --- a/packages/backend/src/QueryEngine.test.ts +++ b/packages/backend/src/QueryEngine.test.ts @@ -12,7 +12,7 @@ const params = { describe('QueryEngine', () => { it('registerQueries + executeRegisteredQuery', async () => { - queryEngine.registerQueries([() => findFilmActor(false).maybe()]); + queryEngine.registerQueries([findFilmActor(false).maybe()]); const parameterizedQueryResult = await queryEngine.executeRegisteredQueryAndWait({ diff --git a/packages/backend/src/QueryEngine.ts b/packages/backend/src/QueryEngine.ts index ddbaf9c0..04619a95 100644 --- a/packages/backend/src/QueryEngine.ts +++ b/packages/backend/src/QueryEngine.ts @@ -1,5 +1,11 @@ import { Pool } from 'pg'; -import { Query, QueryResult, Table } from '@synthql/queries'; +import { + AnyQuery, + Query, + QueryResult, + RegisteredQueryRequestBody, + Table, +} from '@synthql/queries'; import { composeQuery } from './execution/executors/PgExecutor/composeQuery'; import { QueryPlan, collectLast } from '.'; import { QueryProvider } from './QueryProvider'; @@ -9,7 +15,7 @@ import { QueryProviderExecutor } from './execution/executors/QueryProviderExecut import { PgExecutor } from './execution/executors/PgExecutor'; import { generateLast } from './util/generators/generateLast'; import { SynthqlError } from './SynthqlError'; -import { QueryFunction, QueryStore } from './QueryStore'; +import { QueryStore } from './QueryStore'; export interface QueryEngineProps { /** @@ -194,16 +200,11 @@ export class QueryEngine { ); } - // TODO: fix generic types for input and return types - // Currently returning `AsyncGenerator` executeRegisteredQuery< TTable extends Table, TQuery extends Query, >( - { - queryId, - params, - }: { queryId: string; params: Record }, + { queryId, params }: RegisteredQueryRequestBody, opts?: { /** * The name of the database schema to @@ -224,23 +225,17 @@ export class QueryEngine { params, }); - // TODO: Remove this 'as any', after fixing types - return this.execute(query as any, { + return this.execute(query as TQuery, { schema: opts?.schema ?? this.schema, returnLastOnly: true, }); } - // TODO: fix generic types for input and return types - // Currently returning `AsyncGenerator` executeRegisteredQueryAndWait< TTable extends Table, TQuery extends Query, >( - { - queryId, - params, - }: { queryId: string; params: Record }, + { queryId, params }: RegisteredQueryRequestBody, opts?: { /** * The name of the database schema to @@ -252,7 +247,7 @@ export class QueryEngine { }, ): Promise> { return collectLast( - this.executeRegisteredQuery( + this.executeRegisteredQuery( { queryId, params }, { schema: opts?.schema ?? this.schema, @@ -284,11 +279,9 @@ export class QueryEngine { } } - // TODO: fix the callback return type from AnyQuery - // to some version of TQuery (Query) - registerQueries(queryFns: Array) { - for (const queryFn of queryFns) { - this.queryStore.set(queryFn); + registerQueries(queries: Array) { + for (const query of queries) { + this.queryStore.set(query); } } } diff --git a/packages/backend/src/QueryStore.ts b/packages/backend/src/QueryStore.ts index 8caab91e..f49e611f 100644 --- a/packages/backend/src/QueryStore.ts +++ b/packages/backend/src/QueryStore.ts @@ -5,10 +5,8 @@ import { } from '@synthql/queries'; import { SynthqlError } from './SynthqlError'; -export type QueryFunction = (...params: unknown[]) => AnyQuery; - export class QueryStore { - private queries: Map; + private queries: Map; constructor() { this.queries = new Map(); @@ -28,14 +26,12 @@ export class QueryStore { queryId: string; params: Record; }): AnyQuery { - const queryFn = this.queries.get(queryId); + const query = this.queries.get(queryId); - if (!queryFn) { + if (!query) { throw SynthqlError.createQueryNotRegisteredError({ queryId }); } - const query = queryFn(); - // Check if all required parameters are provided const missingParams: string[] = []; @@ -75,9 +71,7 @@ export class QueryStore { * Throws an error if a query with the * same identifier already exists. */ - set(queryFn: QueryFunction): void { - const query = queryFn(); - + set(query: AnyQuery): void { if (!query.hash) { throw SynthqlError.createQueryMissingHashError({ query, @@ -90,6 +84,6 @@ export class QueryStore { }); } - this.queries.set(query.hash, queryFn); + this.queries.set(query.hash, query); } } diff --git a/packages/backend/src/execution/executors/PgExecutor/queryBuilder/exp.ts b/packages/backend/src/execution/executors/PgExecutor/queryBuilder/exp.ts index 3f300c44..7b288d2b 100644 --- a/packages/backend/src/execution/executors/PgExecutor/queryBuilder/exp.ts +++ b/packages/backend/src/execution/executors/PgExecutor/queryBuilder/exp.ts @@ -262,7 +262,7 @@ export class SqlBuilder { addAs(as: As) { const [_, exp, alias] = as; - // TODO validate that alias is a valid alias + // TODO: validate that alias is a valid alias return this.addBuilder(compileExp(exp)).space().add(`as "${alias}" `); } diff --git a/packages/docs/docs/200-security.md b/packages/docs/docs/200-security.md index 4fea0bc3..2477902e 100644 --- a/packages/docs/docs/200-security.md +++ b/packages/docs/docs/200-security.md @@ -26,7 +26,7 @@ What this means is that the `QueryEngine` will only allow queries on the `users` This behaviour can be disabled with the `dangerouslyAllowUnregisteredQueries` option. ```ts -const queryEngine = new QueryEngine({..., dangerouslyAllowUnregisteredQueries: true}); +const queryEngine = new QueryEngine({ ..., dangerouslyAllowUnregisteredQueries: true }); ``` ## Restricting access to tables and columns diff --git a/packages/docs/static/reference/assets/navigation.js b/packages/docs/static/reference/assets/navigation.js index efe3cb08..6707037e 100644 --- a/packages/docs/static/reference/assets/navigation.js +++ b/packages/docs/static/reference/assets/navigation.js @@ -1 +1 @@ -window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE5WX204bMRBA/2WfQym00JY3LpGgrYAmqFWFEDLeCevieDe2FyWq+u/V3nd9GZvX7PEZX8aT8f3fRMNWJyfJE6EvINJ9JWkySwqis+QkWedpyUHttx8flaTvMr3mySx5YSJNTg5nCc0YTyWI5OS+l/0oQe7m4pkJGGSUE6UM2Qicig8OP/+b9b7lTuhsw+dS5hIXjknMOAp8K/NCDVYmNMgVof6Z1gOMfTg6NuW3nIg4a0ViOppzDlR/J0oPwlUpqGa5mPpG6NR4/HEiXBe5gjp42DiwlvJhLOXMnTyUM0fijEZmRKQc5B5sCwlKuS0t9NhC0alIJRAN82ZUmx2Xjcu1dFcYRIHuSLcuAVuNL6oiold0DVvtW4jeFS6tPcSI9P7Lp4Oj8ZnYIxawKWGcgdGx2pGhkM02Y6uzj6mP6huMHhATWuaqAOo5nuF79OE8gwBJNLgmbeg6FJ3ipgTJwHMn2o/RkzvPebkWF7By1qWxrSexutRDVUEEqRm4y6hTPIyJCvEGMypcwDNTGiSkdUWzktojdw8LBCo5kW+NYo7BQixpBmsStDYYJrojTxxi0qIDMdmp2DVHYVaKsaiHQkXhVOwuzgKmi7MIi/Fn5xQ5/uScrnobAq6aiXVN9t6vs3feMp4xQeTupsB0HRPrqqpUbv3JuIwNGfKeE5kyQTjT6IGMsKAxmHBx2dZQC1iBBEHRIzbQOPPdroiQVlSc7yfhZYSwxkLGK0F5maK2FgmZvuZM4BnYECFP8MpG3demxSaSrEHbrZKl68kor6cpsqyRLVDLqpLHKCssZFzACj+KGghZllC9KjBNQ4Q8wcIZVTV/ZSBRSw1EWc45KVXY1WDBenl1fbr4/XhzO1+c3t0sloP2lUhWrcyomgY/9X849LctIbOB4+K+5Yiw9iyibBtwx5Vza00eUdOcuxrrsY3mHHn5pn+eDi+JykKWjkNUGVGZ9wU96cw7EJEx5StTbqfJo2qjBviMjkpgifDm2W8Od892KH/7jMTB++dpEF0/vxZAS6nYK/DgSdojEH1RnU3IWEOIZBOTYDW0d4C+IyUQ3yu3/hT9hmxv63ku6h9cL4ZBOIXRt0xD1kf3Dax/fUvZgaGarDzTHUrRoJ6y5oY6a5zMX1nqvq7WlDsYzXvvPrjEFo6o1VvEBuzIrUFcKmhngTsHLkpmnRjudKZYfQce/gOI0+6M5BYAAA==" \ No newline at end of file +window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE52X204bMRBA/2WfaSlpoW3eIESCtgKaoFYVQsh4J6yL493YXpSo6r9Xe8lefBmbvmaPz9ie8cS++5No2OpkmjwS+gwiPVSSJgdJQXSWTJN1npYc1GH78UFJ+jbTa54cJM9MpMl0cpDQjPFUgkimd53sewlyNxdPTEAvo5woZcgG4Fh8NPn096DzLXdCZxs+lzKXuHBIYsZB4BuZF6q3MqFBrgj1z7QeYOzD8Ykpv+FExFkrEtPRnHOg+htRuheuSkE1y8XYN0DHxpMPI+G6yBXUwcPGnrWU90MpZ+7ioZw5CmcwMiMi5SDfwLaQoJTb0kIPLRRdilQC0TBvRrXVcdG4XEt3hUEU6I7s1yVgq/FFVUT0iq5gq30L0bvCpbWHGJHeff54dDzMiT1iAZsShhUYHasdGQrZbDO2OjtNXVTfYDRBTGiZqwKoJz399+jkPIEASTS4Jm3o9ig6xU0JkoHnTLQfoyc3y3m5Fuewcvaloa0jsb7UQVVDBKkZuNuoU9yPiQrxCjMqXMATUxokpHVHs4raI3cPe32gszzd/WewamggYMmJfO2yzDFYiCXNYE2C1gbDRLfkkUNMHe5BTHYqdk3uzdY0FHVQqAudit35WcB0fhZhMf5dnSLHv6rTVW9DwFUzsa7R3vt19s5bxjMmiNxdF5huz8S6qraYW/9qLmNDhrwzIlMmCGcaTcgACxqDBRdXbQ21gBVIEBRNsYHGmW93RYS0ouJ8PwgvI4Q1FjJeCsrLFLW1SMj0JWcCr8CGCHmCRzbqvDZ3eiLJGrR9N7N0HRnl9dzCLGvknatlVcljlBUWMi5ghaeiBkKWJVTPGEzTECFPsHFGdc2fGUjUUgNRlhknpQq7GizYLy+vThe/Hq5v5ovT2+vFste+EMmqlRld0+DH/vcT//UlZDZwXNxdOSKsHYso2xu/48i5tSaPqGnOXTf5oY3mHHlqp78fJxdEZSHLnkNUGVGZ98k+egrsQUTGlK9NuZ0mj6qNHuAzOjqBJcJv635z+Lpuh/Jfn5E4+P15HETX770F0FIq9gI8mEl7BKIvqtyEjDWESDYxBVZDb47Qh6sE4ntW15+iH63taZ3lov7B9WLohWMYfcs0ZJ26r2D961vKPRjqycoz3b4V9eoxa26os8fJ/IWl7uNqTXkPo3Xv3QeX2MIRtXqN2IAdtdWLSwXtLHBnz0XJrIzhTmeJ1Wfg/h9DXy+qVRcAAA==" \ No newline at end of file diff --git a/packages/docs/static/reference/assets/search.js b/packages/docs/static/reference/assets/search.js index af4f903d..7a9b6fe3 100644 --- a/packages/docs/static/reference/assets/search.js +++ b/packages/docs/static/reference/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file diff --git a/packages/handler-express/src/createExpressSynthqlHandler.ts b/packages/handler-express/src/createExpressSynthqlHandler.ts index bcffd21e..78ec9731 100644 --- a/packages/handler-express/src/createExpressSynthqlHandler.ts +++ b/packages/handler-express/src/createExpressSynthqlHandler.ts @@ -3,6 +3,7 @@ import { collectLast, QueryEngine, SynthqlError } from '@synthql/backend'; import { isRegisteredQueryRequest, isRegularQueryRequest, + Query, } from '@synthql/queries'; /** @@ -64,23 +65,11 @@ async function executeSynthqlRequest( // const validatedQuery = await tryValidateSynthqlQuery(query); // Execute the query, but just to get the initial generator - const resultGenerator = isRegisteredQueryRequest(body) - ? await tryExecuteRegisteredQuery( - queryEngine, - { queryId: body.queryId, params: body.params }, - headers.returnLastOnly, - ) - : isRegularQueryRequest(body) - ? await tryExecuteQuery( - queryEngine, - body.query, - headers.returnLastOnly, - ) - : await tryExecuteQuery( - queryEngine, - body, - headers.returnLastOnly, - ); + const resultGenerator = await tryExecuteQuery( + queryEngine, + body, + headers.returnLastOnly, + ); // Now that we have the generator, we want to iterate over the items // and depending on `returnLastOnly`, we will write the status code @@ -111,21 +100,26 @@ async function tryParseRequest(req: Request) { async function tryExecuteQuery( queryEngine: QueryEngine, - query: any, - returnLastOnly: boolean, -) { - return queryEngine.execute(query, { returnLastOnly }); -} - -async function tryExecuteRegisteredQuery( - queryEngine: QueryEngine, - { queryId, params }: { queryId: string; params: Record }, + queryOrBody: any, returnLastOnly: boolean, ) { - return queryEngine.executeRegisteredQuery( - { queryId, params }, - { returnLastOnly }, - ); + if (isRegisteredQueryRequest(queryOrBody)) { + return queryEngine.executeRegisteredQuery( + { + queryId: queryOrBody.queryId, + params: queryOrBody.params, + }, + { + returnLastOnly, + }, + ); + } else if (isRegularQueryRequest(queryOrBody)) { + return queryEngine.execute(queryOrBody.query as Query, { + returnLastOnly, + }); + } else { + return queryEngine.execute(queryOrBody, { returnLastOnly }); + } } async function writeResponseBody( diff --git a/packages/handler-next/src/createNextSynthqlHandler.ts b/packages/handler-next/src/createNextSynthqlHandler.ts index 75dd7da1..d15411b5 100644 --- a/packages/handler-next/src/createNextSynthqlHandler.ts +++ b/packages/handler-next/src/createNextSynthqlHandler.ts @@ -3,6 +3,7 @@ import { collectLast, QueryEngine, SynthqlError } from '@synthql/backend'; import { isRegisteredQueryRequest, isRegularQueryRequest, + Query, } from '@synthql/queries'; import { ReadableStream } from 'stream/web'; @@ -73,23 +74,11 @@ async function executeSynthqlRequest( // const validatedQuery = await tryValidateSynthqlQuery(query); // Execute the query, but just to get the initial generator - const resultGenerator = isRegisteredQueryRequest(body) - ? await tryExecuteRegisteredQuery( - queryEngine, - { queryId: body.queryId, params: body.params }, - headers.returnLastOnly, - ) - : isRegularQueryRequest(body) - ? await tryExecuteQuery( - queryEngine, - body.query, - headers.returnLastOnly, - ) - : await tryExecuteQuery( - queryEngine, - body, - headers.returnLastOnly, - ); + const resultGenerator = await tryExecuteQuery( + queryEngine, + body, + headers.returnLastOnly, + ); // Now that we have the generator, we want to iterate over // the items and depending on `returnLastOnly`, we will @@ -123,21 +112,26 @@ async function tryParseRequest(req: NextSynthqlHandlerRequest) { async function tryExecuteQuery( queryEngine: QueryEngine, - query: any, - returnLastOnly: boolean, -) { - return queryEngine.execute(query, { returnLastOnly }); -} - -async function tryExecuteRegisteredQuery( - queryEngine: QueryEngine, - { queryId, params }: { queryId: string; params: Record }, + queryOrBody: any, returnLastOnly: boolean, ) { - return queryEngine.executeRegisteredQuery( - { queryId, params }, - { returnLastOnly }, - ); + if (isRegisteredQueryRequest(queryOrBody)) { + return queryEngine.executeRegisteredQuery( + { + queryId: queryOrBody.queryId, + params: queryOrBody.params, + }, + { + returnLastOnly, + }, + ); + } else if (isRegularQueryRequest(queryOrBody)) { + return queryEngine.execute(queryOrBody.query as Query, { + returnLastOnly, + }); + } else { + return queryEngine.execute(queryOrBody, { returnLastOnly }); + } } async function writeResponseBody( diff --git a/packages/queries/src/index.ts b/packages/queries/src/index.ts index 72c1eb63..9d46409f 100644 --- a/packages/queries/src/index.ts +++ b/packages/queries/src/index.ts @@ -18,8 +18,8 @@ export * from './types/Where'; export * from './types/WhereClause'; export * from './validators/isQueryParameter'; export * from './validators/isRefOp'; -export * from './validators/isRegisteredQuery'; -export * from './validators/isRegularQuery'; +export * from './validators/isRegisteredQueryRequest'; +export * from './validators/isRegularQueryRequest'; export * from './util/hashQuery'; export * from './util/iterateRecursively'; export { col } from './col'; diff --git a/packages/queries/src/types/QueryRequest.ts b/packages/queries/src/types/QueryRequest.ts index abff8f4b..4c6e1362 100644 --- a/packages/queries/src/types/QueryRequest.ts +++ b/packages/queries/src/types/QueryRequest.ts @@ -8,10 +8,15 @@ export interface RegularQueryRequest { query: AnyQuery; } -export interface RegisteredQueryRequest { - type: typeof RegisteredQuery; +export interface RegisteredQueryRequestBody { queryId: string; params: Record; } +export interface RegisteredQueryRequest { + type: typeof RegisteredQuery; + queryId: RegisteredQueryRequestBody['queryId']; + params: RegisteredQueryRequestBody['params']; +} + export type QueryRequest = RegularQueryRequest | RegisteredQueryRequest; diff --git a/packages/queries/src/util/iterateRecursively.ts b/packages/queries/src/util/iterateRecursively.ts index 257eb98f..e14ff6fa 100644 --- a/packages/queries/src/util/iterateRecursively.ts +++ b/packages/queries/src/util/iterateRecursively.ts @@ -23,31 +23,17 @@ export function iterateRecursively( visitor: (traversable: Traversable, path: string[]) => void, path: string[] = [], ): void { - // Apply the visitor to the current traversable visitor(traversable, path); if ( - typeof traversable === 'object' && - traversable !== null && - !(traversable instanceof Date) + traversable === null || + typeof traversable !== 'object' || + traversable instanceof Date ) { - if (Array.isArray(traversable)) { - // If it's an array, iterate over each element - // with its index as the key in the path - traversable.forEach((item, index) => { - iterateRecursively(item, visitor, [...path, String(index)]); - }); - } else { - // If it's an object, iterate over each - // property with its key in the path - for (const key in traversable) { - if (Object.prototype.hasOwnProperty.call(traversable, key)) { - iterateRecursively(traversable[key], visitor, [ - ...path, - String(key), - ]); - } - } - } + return; + } + + for (const [key, value] of Object.entries(traversable)) { + iterateRecursively(value, visitor, [...path, key]); } } diff --git a/packages/queries/src/validators/isRegisteredQuery.ts b/packages/queries/src/validators/isRegisteredQueryRequest.ts similarity index 100% rename from packages/queries/src/validators/isRegisteredQuery.ts rename to packages/queries/src/validators/isRegisteredQueryRequest.ts diff --git a/packages/queries/src/validators/isRegularQuery.ts b/packages/queries/src/validators/isRegularQueryRequest.ts similarity index 100% rename from packages/queries/src/validators/isRegularQuery.ts rename to packages/queries/src/validators/isRegularQueryRequest.ts From 1c6db94feb25c14feeb50a49aa06dc17340a0758 Mon Sep 17 00:00:00 2001 From: Jim Ezesinachi Date: Fri, 27 Sep 2024 08:50:58 +0000 Subject: [PATCH 15/15] fixes Signed-off-by: Jim Ezesinachi --- .../queries/src/util/iterateRecursively.ts | 24 ++----------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/packages/queries/src/util/iterateRecursively.ts b/packages/queries/src/util/iterateRecursively.ts index e14ff6fa..791a00a3 100644 --- a/packages/queries/src/util/iterateRecursively.ts +++ b/packages/queries/src/util/iterateRecursively.ts @@ -1,26 +1,6 @@ -// TODO: Unite/move this type with the one of the same -// name already being used in `backend` and `queries` -// See: -// packages/backend/src/execution/executors/PgExecutor/queryBuilder/exp.ts -// packages/queries/src/expression/Expression.ts -type Primitive = - | string - | number - | boolean - | null - | undefined - | symbol - | bigint - | Date; - -type Traversable = - | Primitive - | { [key: string | number]: Traversable } - | Array; - -export function iterateRecursively( +export function iterateRecursively( traversable: T, - visitor: (traversable: Traversable, path: string[]) => void, + visitor: (traversable: unknown, path: string[]) => void, path: string[] = [], ): void { visitor(traversable, path);