Skip to content

Commit

Permalink
Fix directly calling operations on the frontend (#1992)
Browse files Browse the repository at this point in the history
  • Loading branch information
sodic committed May 7, 2024
1 parent 1e78226 commit 1699862
Show file tree
Hide file tree
Showing 199 changed files with 3,691 additions and 2,524 deletions.
21 changes: 10 additions & 11 deletions waspc/data/Generator/templates/sdk/wasp/auth/useAuth.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
{{={= =}=}}
import { deserialize as superjsonDeserialize } from 'superjson'
import { useQuery, addMetadataToQuery } from 'wasp/client/operations'
import { useQuery, buildAndRegisterQuery } from 'wasp/client/operations'
import type { QueryFunction, Query } from 'wasp/client/operations/rpc'
import { api, handleApiError } from 'wasp/client/api'
import { HttpMethod } from 'wasp/client'
import type { AuthUser } from '../server/auth/user.js'
import { UseQueryResult } from '@tanstack/react-query'

// PUBLIC API
export const getMe: () => Promise<AuthUser | null> = createUserGetter()
export const getMe: Query<void, AuthUser | null> = createUserGetter()

// PUBLIC API
export default function useAuth(queryFnArgs?: unknown, config?: any): UseQueryResult<AuthUser> {
return useQuery(getMe, queryFnArgs, config)
}
export default function useAuth(): UseQueryResult<AuthUser | null> {
return useQuery(getMe)
}

function createUserGetter() {
function createUserGetter(): Query<void, AuthUser | null> {
const getMeRelativePath = 'auth/me'
const getMeRoute = { method: HttpMethod.Get, path: `/${getMeRelativePath}` }
async function getMe(): Promise<AuthUser | null> {
const getMe: QueryFunction<void, AuthUser | null> = async () => {
try {
const response = await api.get(getMeRoute.path)
return superjsonDeserialize(response.data)
Expand All @@ -30,11 +31,9 @@ function createUserGetter() {
}
}

addMetadataToQuery(getMe, {
relativeQueryPath: getMeRelativePath,
return buildAndRegisterQuery(getMe, {
queryCacheKey: [getMeRelativePath],
queryRoute: getMeRoute,
entitiesUsed: {=& entitiesGetMeDependsOn =},
})

return getMe
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { Expand, _Awaited, _ReturnType } from 'wasp/universal/types'
import { type Action } from '../core.js'
import type { _Awaited, _ReturnType } from 'wasp/universal/types'
import type { OperationRpcFor, GenericBackendOperation } from '../rpc.js'
import { callOperation, makeOperationRoute } from '../internal/index.js'
import {
registerActionInProgress,
registerActionDone,
} from '../internal/resources.js'

// PRIVATE API
export function createAction<BackendAction extends GenericBackendAction>(
export function createAction<BackendAction extends GenericBackendOperation>(
relativeActionRoute: string,
entitiesUsed: unknown[]
): ActionFor<BackendAction> {
Expand Down Expand Up @@ -41,8 +41,5 @@ export function createAction<BackendAction extends GenericBackendAction>(
}

// PRIVATE API
export type ActionFor<BackendAction extends GenericBackendAction> =
Action<Parameters<BackendAction>[0], _Awaited<_ReturnType<BackendAction>>>


type GenericBackendAction = (args: never, context: any) => unknown
export type ActionFor<BackendAction extends GenericBackendOperation> =
OperationRpcFor<BackendAction>
Original file line number Diff line number Diff line change
Expand Up @@ -7,91 +7,31 @@ import {
useQuery as rqUseQuery,
UseQueryResult,
} from "@tanstack/react-query";
import { Action, Query } from "./rpc";
import { makeQueryCacheKey } from "./queries/core";
export { configureQueryClient } from "./queryClient";

// PRIVATE API (but should maybe be public, users use values of this type)
export type Query<Input, Output> = {
(queryCacheKey: string[], args: Input): Promise<Output>;
};

// PUBLIC API
export function useQuery<Input, Output>(
queryFn: Query<Input, Output>,
query: Query<Input, Output>,
queryFnArgs?: Input,
options?: any
): UseQueryResult<Output, Error>;

// PUBLIC API
export function useQuery(queryFn, queryFnArgs, options) {
if (typeof queryFn !== "function") {
throw new TypeError("useQuery requires queryFn to be a function.");
): UseQueryResult<Output, Error> {
if (typeof query !== 'function') {
throw new TypeError('useQuery requires queryFn to be a function.')
}
if (!queryFn.queryCacheKey) {
throw new TypeError(
"queryFn needs to have queryCacheKey property defined."
);

if (!query.queryCacheKey) {
throw new TypeError('queryFn needs to have queryCacheKey property defined.')
}

const queryKey =
queryFnArgs !== undefined
? [...queryFn.queryCacheKey, queryFnArgs]
: queryFn.queryCacheKey;
return rqUseQuery({
queryKey,
queryFn: () => queryFn(queryKey, queryFnArgs),
queryKey: makeQueryCacheKey(query, queryFnArgs),
queryFn: () => query(queryFnArgs),
...options,
});
})
}

// PRIVATE API (but should maybe be public, users use values of this type)
export type Action<Input, Output> = [Input] extends [never]
? (args?: unknown) => Promise<Output>
: (args: Input) => Promise<Output>;

// PRIVATE API (but should maybe be public, users define values of this type)
/**
* An options object passed into the `useAction` hook and used to enhance the
* action with extra options.
*
*/
export type ActionOptions<ActionInput> = {
optimisticUpdates: OptimisticUpdateDefinition<ActionInput, any>[];
};

// PUBLIC API
/**
* A documented (public) way to define optimistic updates.
*/
export type OptimisticUpdateDefinition<ActionInput, CachedData> = {
getQuerySpecifier: GetQuerySpecifier<ActionInput, CachedData>;
updateQuery: UpdateQuery<ActionInput, CachedData>;
};

// PRIVATE API (but should maybe be public, users define values of this type)
/**
* A function that takes an item and returns a Wasp Query specifier.
*/
export type GetQuerySpecifier<ActionInput, CachedData> = (
item: ActionInput
) => QuerySpecifier<unknown, CachedData>;

// PRIVATE API (but should maybe be public, users define values of this type)
/**
* A function that takes an item and the previous state of the cache, and returns
* the desired (new) state of the cache.
*/
export type UpdateQuery<ActionInput, CachedData> = (
item: ActionInput,
oldData: CachedData | undefined
) => CachedData;

// PRIVATE API (but should maybe be public, users define values of this type)
/**
* A public query specifier used for addressing Wasp queries. See our docs for details:
* https://wasp-lang.dev/docs/language/features#the-useaction-hook.
*/
export type QuerySpecifier<Input, Output> = [Query<Input, Output>, ...any[]];

// PUBLIC API
/**
* A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates).
Expand Down Expand Up @@ -133,6 +73,48 @@ export function useAction<Input = unknown, Output = unknown>(
return (args) => mutation.mutateAsync(args);
}

// PUBLIC API
/**
* A documented (public) way to define optimistic updates.
*/
export type OptimisticUpdateDefinition<ActionInput, CachedData> = {
getQuerySpecifier: GetQuerySpecifier<ActionInput, CachedData>;
updateQuery: UpdateQuery<ActionInput, CachedData>;
};

/**
* An options object passed into the `useAction` hook and used to enhance the
* action with extra options.
*
*/
type ActionOptions<ActionInput> = {
optimisticUpdates: OptimisticUpdateDefinition<ActionInput, any>[];
};

/**
* A function that takes an item and returns a Wasp Query specifier.
*/
type GetQuerySpecifier<ActionInput, CachedData> = (
item: ActionInput
) => QuerySpecifier<unknown, CachedData>;

/**
* A function that takes an item and the previous state of the cache, and returns
* the desired (new) state of the cache.
*/
type UpdateQuery<ActionInput, CachedData> = (
item: ActionInput,
oldData: CachedData | undefined
) => CachedData;

// PRIVATE API (but should maybe be public, users define values of this type)
/**
* A public query specifier used for addressing Wasp queries. See our docs for details:
* https://wasp-lang.dev/docs/language/features#the-useaction-hook.
*/
type QuerySpecifier<Input, Output> = [Query<Input, Output>, ...any[]];


/**
* An internal (undocumented, private, desugared) way of defining optimistic updates.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export {
useQuery,
// PUBLIC API
type OptimisticUpdateDefinition,
} from './core'
} from './hooks'

export {
// PUBLIC API
Expand Down
Original file line number Diff line number Diff line change
@@ -1,53 +1,83 @@
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 {
GenericBackendOperation,
GenericOperationRpc,
OperationRpcFor,
Query,
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 (used in the SDK)
export function makeQueryCacheKey<Input, Output>(
query: Query<Input, Output>,
payload: Input
): (string | Input)[] {
return payload !== undefined ?
[...query.queryCacheKey, payload]
: query.queryCacheKey
}

// 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(
const queryCacheKey = makeQueryCacheKey(queryFn as QueryFor<BackendQuery>, queryArgs)
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<QF extends GenericOperationRpc>(
queryFn: QF,
{ queryCacheKey, queryRoute, entitiesUsed }:
{ queryCacheKey: string[], queryRoute: Route, entitiesUsed: string[] }
): QueryForFunction<QF> {
const query = queryFn as QueryForFunction<QF>

query.queryCacheKey = queryCacheKey
query.route = queryRoute
addResourcesUsedByQuery(query.queryCacheKey, entitiesUsed)

return query
}

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)
/**
* Constructs the client Query object type from the type of the Query's definition
* on the backend.
*/
export type QueryFor<BackendQuery extends GenericBackendOperation> =
QueryForFunction<QueryFunctionFor<BackendQuery>>

/**
* Constructs the client Query function type from the type of the Query's
* definition on the backend.
*/
type QueryFunctionFor<BackendQuery extends GenericBackendOperation> =
OperationRpcFor<BackendQuery>

type GenericBackendQuery = (args: never, context: any) => unknown
/**
* Returns the appropriate client Query object type for the provided client
* Query function type.
*/
type QueryForFunction<QF extends GenericOperationRpc> = QF & QueryMetadata
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ export const {= operationName =}: QueryFor<{= operationTypeName =}> = createQuer
)
{=/ queries =}
// PRIVATE API
export { addMetadataToQuery } from './core'
// PRIVATE API (used in SDK)
export { buildAndRegisterQuery } from './core'
Loading

0 comments on commit 1699862

Please sign in to comment.