-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix directly calling operations on the frontend #1992
Changes from 18 commits
d8651a8
c7d8adf
c309ad3
83d24dc
7f5876b
6bc94cf
e8273c9
43a9d71
6249c97
f9b674a
a659278
e81f2e4
9d4e2d1
ccde43a
15224f8
e11581e
e4f175f
2b9f022
3c8aac5
3271aaa
ee25c74
dcfb9c9
fc5a20a
d7e1591
d52a931
74d5e2a
c4de989
606ee83
bfc6fe8
ab6f014
590c33a
1fd0042
8e93092
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,64 @@ | ||
import { Route } from 'wasp/client' | ||
import type { Expand, _Awaited, _ReturnType } from 'wasp/universal/types' | ||
import { type Query } from '../core.js' | ||
import type { _Awaited, _ReturnType } from 'wasp/universal/types' | ||
import type { | ||
ClientOperation, | ||
GenericBackendOperation, | ||
OperationRpcFor, | ||
QueryFunction, | ||
QueryMetadata, | ||
} from '../rpc.js' | ||
import { callOperation, makeOperationRoute } from '../internal/index.js' | ||
import { | ||
addResourcesUsedByQuery, | ||
getActiveOptimisticUpdates, | ||
} from '../internal/resources' | ||
|
||
export function createQuery<BackendQuery extends GenericBackendQuery>( | ||
// PRIVATE API (unsed in SDK) | ||
export function createQuery<BackendQuery extends GenericBackendOperation>( | ||
relativeQueryPath: string, | ||
entitiesUsed: string[] | ||
): QueryFor<BackendQuery> { | ||
const queryRoute = makeOperationRoute(relativeQueryPath) | ||
const queryCacheKey = [relativeQueryPath] | ||
|
||
async function query(queryKey, queryArgs) { | ||
const queryFn: QueryFunctionFor<BackendQuery> = async (queryArgs) => { | ||
const serverResult = await callOperation(queryRoute, queryArgs) | ||
return getActiveOptimisticUpdates(queryKey).reduce( | ||
return getActiveOptimisticUpdates(queryCacheKey).reduce( | ||
(result, update) => update(result), | ||
serverResult, | ||
) | ||
} | ||
|
||
addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }) | ||
|
||
return query | ||
return buildAndRegisterQuery( | ||
queryFn, | ||
{ queryCacheKey, queryRoute, entitiesUsed }, | ||
) | ||
} | ||
|
||
// PRIVATE API | ||
export function addMetadataToQuery( | ||
query: (...args: any[]) => Promise<unknown>, | ||
metadata: { | ||
relativeQueryPath: string | ||
queryRoute: Route | ||
entitiesUsed: string[] | ||
} | ||
): void | ||
|
||
// PRIVATE API | ||
export function addMetadataToQuery( | ||
query, | ||
{ relativeQueryPath, queryRoute, entitiesUsed } | ||
) { | ||
query.queryCacheKey = [relativeQueryPath] | ||
// PRIVATE API (used in SDK) | ||
export function buildAndRegisterQuery<Input, Output>( | ||
queryFn: ClientOperation<Input, Output>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How not use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice catch. I introduced |
||
{ queryCacheKey, queryRoute, entitiesUsed }: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I took the creation of the |
||
{ queryCacheKey: string[], queryRoute: Route, entitiesUsed: string[] } | ||
): QueryForFunction<typeof queryFn> { | ||
const query = queryFn as QueryForFunction<typeof queryFn> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice cast |
||
|
||
query.queryCacheKey = queryCacheKey | ||
query.route = queryRoute | ||
addResourcesUsedByQuery(query.queryCacheKey, entitiesUsed) | ||
|
||
return query | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: extra new line |
||
} | ||
|
||
export type QueryFor<BackendQuery extends GenericBackendQuery> = | ||
Query<Parameters<BackendQuery>[0], _Awaited<_ReturnType<BackendQuery>>> | ||
// PRIVATE API (but should maybe be public, users define values of this type) | ||
export type QueryFor<BackendQuery extends GenericBackendOperation> = | ||
QueryForFunction<QueryFunctionFor<BackendQuery>> | ||
|
||
type QueryFunctionFor<BackendQuery extends GenericBackendOperation> = | ||
OperationRpcFor<BackendQuery> | ||
|
||
// PRIVATE API (needed in SDK) | ||
type QueryForFunction<QF extends QueryFunction<never, unknown>> = | ||
QF & QueryMetadata | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I decided to avoid duplicating the same code for Queries and Actions this time around, mostly because that's what caused an incorrect type in RPC calls for queries. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I found this block hard to parse, maybe put some why comment on top to make it easier for us in the future to get back into this code. |
||
|
||
type GenericBackendQuery = (args: never, context: any) => unknown |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,5 +12,5 @@ export const {= operationName =}: QueryFor<{= operationTypeName =}> = createQuer | |
) | ||
{=/ queries =} | ||
|
||
// PRIVATE API | ||
export { addMetadataToQuery } from './core' | ||
// PRIVATE API (used in SDK) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If used only in SDK, do you need to expose in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, that's what we'll do as part of #1922. But for now, yes (since other parts of SDK use it as a package import). |
||
export { buildAndRegisterQuery } from './core' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { type Route } from "wasp/client"; | ||
import type { | ||
_Awaited, | ||
_ReturnType, | ||
} from "wasp/universal/types" | ||
|
||
// PRIVATE API (but should maybe be public, users define values of this type) | ||
export type Query<Input, Output> = QueryFunction<Input, Output> & QueryMetadata | ||
|
||
// PRIVATE API (but should maybe be public, users define values of this type) | ||
export type Action<Input, Output> = ClientOperation<Input, Output> | ||
sodic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// PRIVATE API | ||
export type QueryFunction<Input, Output> = ClientOperation<Input, Output> | ||
|
||
// PRIVATE API | ||
export type QueryMetadata = { | ||
queryCacheKey: string[] | ||
route: Route | ||
} | ||
sodic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// PRIVATE API (needed in SDK) | ||
export type OperationRpcFor<BackendOperation extends GenericBackendOperation> = | ||
Parameters<BackendOperation> extends [] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TS Playground link explaining why this check was necessary. |
||
? ClientOperation<void, _Awaited<_ReturnType<BackendOperation>>> | ||
: ClientOperation< | ||
Parameters<BackendOperation>[0], | ||
_Awaited<_ReturnType<BackendOperation>> | ||
> | ||
|
||
// PRIVATE API (but should maybe be public, users use values of this type) | ||
// Read this to understand the type: https://github.com/wasp-lang/wasp/pull/1090#discussion_r1159732471 | ||
export type ClientOperation<Input, Output> = [Input] extends [never] | ||
? (args?: unknown) => Promise<Output> | ||
: (args: Input) => Promise<Output>; | ||
|
||
// PRIVATE API (needed in SDK) | ||
export type GenericBackendOperation = (args: never, context: any) => unknown |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,12 @@ | ||
import { useSocket } from "wasp/client/webSocket"; | ||
import { Link } from "wasp/client/router"; | ||
import { logout, useAuth } from "wasp/client/auth"; | ||
import { useQuery, getDate } from "wasp/client/operations"; | ||
import { useSocket } from 'wasp/client/webSocket' | ||
import { Link } from 'wasp/client/router' | ||
import { logout, useAuth } from 'wasp/client/auth' | ||
import { useQuery, getDate } from 'wasp/client/operations' | ||
|
||
import './Main.css' | ||
import { getName } from './user' | ||
// Necessary to trigger type tests. | ||
import './TestRpcTypes' | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd recommend maybe creating a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then you could put the helpers in |
||
export function App({ children }: any) { | ||
const { data: user } = useAuth() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a proper function (i.e., a smart constructor) seemed cleaner than having a function with side effects that you might forget to call.