Skip to content

Commit

Permalink
V2: Update query keys for partial matching (#441)
Browse files Browse the repository at this point in the history
  • Loading branch information
timostamm authored Oct 8, 2024
1 parent 3212f35 commit 92e2997
Show file tree
Hide file tree
Showing 13 changed files with 391 additions and 133 deletions.
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,13 +249,12 @@ Any additional `options` you pass to `useMutation` will be merged with the optio
### `createConnectQueryKey`

```ts
function createConnectQueryKey<I extends Message<I>, O extends Message<O>>(
methodDescriptor: Pick<MethodUnaryDescriptor<I, O>, "I" | "name" | "service">,
input?: SkipToken | PartialMessage<I> | undefined,
): ConnectQueryKey<I>;
function createConnectQueryKey<Desc extends DescMethod | DescService>(
params: KeyParams<Desc>,
): ConnectQueryKey;
```

This helper is useful to manually compute the [`queryKey`](https://tanstack.com/query/v4/docs/react/guides/query-keys) sent to TanStack Query. This function has no side effects.
This function is used under the hood of `useQuery` and other hooks to compute a [`queryKey`](https://tanstack.com/query/v4/docs/react/guides/query-keys) for TanStack Query. You can use it to create (partial) keys yourself to filter queries.

### `createConnectInfiniteQueryKey`

Expand Down
22 changes: 14 additions & 8 deletions packages/connect-query/src/call-unary-method.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { create } from "@bufbuild/protobuf";
import type { QueryFunctionContext } from "@tanstack/react-query";
import { useQueries } from "@tanstack/react-query";
import { renderHook, waitFor } from "@testing-library/react";
Expand All @@ -21,30 +22,35 @@ import { callUnaryMethod } from "./call-unary-method.js";
import type { ConnectQueryKey } from "./connect-query-key.js";
import { createConnectQueryKey } from "./connect-query-key.js";
import { defaultOptions } from "./default-options.js";
import { ElizaService } from "./gen/eliza_pb.js";
import type { SayRequest } from "./gen/eliza_pb.js";
import { ElizaService, SayRequestSchema } from "./gen/eliza_pb.js";
import { mockEliza, wrapper } from "./test/test-utils.js";

describe("callUnaryMethod", () => {
it("can be used with useQueries", async () => {
const transport = mockEliza({
sentence: "Response 1",
});
const { result } = renderHook(
() => {
const input: SayRequest = create(SayRequestSchema, {
sentence: "query 1",
});
const [query1] = useQueries({
queries: [
{
queryKey: createConnectQueryKey(ElizaService.method.say, {
sentence: "query 1",
queryKey: createConnectQueryKey({
schema: ElizaService.method.say,
input,
transport,
}),
queryFn: async ({
queryKey,
signal,
}: QueryFunctionContext<ConnectQueryKey>) => {
const transport = mockEliza({
sentence: "Response 1",
});
const res = await callUnaryMethod(
transport,
ElizaService.method.say,
queryKey[2],
input,
{
signal,
},
Expand Down
118 changes: 91 additions & 27 deletions packages/connect-query/src/connect-query-key.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,52 +12,116 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { create } from "@bufbuild/protobuf";
import type { Transport } from "@connectrpc/connect";
import { skipToken } from "@tanstack/react-query";
import { describe, expect, it } from "vitest";

import { createConnectQueryKey } from "./connect-query-key.js";
import { ElizaService, SayRequestSchema } from "./gen/eliza_pb.js";
import { ListRequestSchema, ListService } from "./gen/list_pb.js";
import { createMessageKey } from "./message-key.js";
import { createTransportKey } from "./transport-key.js";

describe("makeQueryKey", () => {
const methodDescriptor = {
input: SayRequestSchema,
name: "name",
parent: ElizaService,
describe("createConnectQueryKey", () => {
const fakeTransport: Transport = {
async stream() {
return Promise.reject(new Error("unexpected"));
},
async unary() {
return Promise.reject(new Error("unexpected"));
},
};

it("makes a query key with input", () => {
const key = createConnectQueryKey(methodDescriptor, {
sentence: "someValue",
it("creates a full key", () => {
const key = createConnectQueryKey({
transport: fakeTransport,
schema: ElizaService.method.say,
input: create(SayRequestSchema, { sentence: "hi" }),
});
expect(key).toStrictEqual([
ElizaService.typeName,
"name",
createMessageKey(SayRequestSchema, { sentence: "someValue" }),
"connect-query",
{
transport: createTransportKey(fakeTransport),
serviceName: "connectrpc.eliza.v1.ElizaService",
methodName: "Say",
cardinality: "finite",
input: createMessageKey(SayRequestSchema, { sentence: "hi" }),
},
]);
});

it("allows empty inputs", () => {
const key = createConnectQueryKey(methodDescriptor);
it("creates a full infinite key", () => {
const key = createConnectQueryKey({
transport: fakeTransport,
schema: ListService.method.list,
input: create(ListRequestSchema, { page: 0n }),
pageParamKey: "page",
cardinality: "infinite",
});
expect(key).toStrictEqual([
ElizaService.typeName,
"name",
createMessageKey(methodDescriptor.input, {}),
"connect-query",
{
transport: createTransportKey(fakeTransport),
serviceName: "ListService",
methodName: "List",
cardinality: "infinite",
input: createMessageKey(ListRequestSchema, {}),
},
]);
});

it("makes a query key with a skipToken", () => {
const key = createConnectQueryKey(methodDescriptor, skipToken);
expect(key).toStrictEqual([
ElizaService.typeName,
"name",
createMessageKey(methodDescriptor.input, {}),
]);
it("allows input: undefined", () => {
const key = createConnectQueryKey({
schema: ElizaService.method.say,
input: undefined,
});
expect(key[1].input).toBeUndefined();
});

it("allows to omit input", () => {
const key = createConnectQueryKey({
schema: ElizaService.method.say,
});
expect(key[1].input).toBeUndefined();
});

it("allows input: skipToken", () => {
const key = createConnectQueryKey({
schema: ElizaService.method.say,
input: skipToken,
});
expect(key[1].input).toBe("skipped");
});

it("generates identical keys when input is empty or the default is explicitly sent", () => {
const key1 = createConnectQueryKey(methodDescriptor, {});
const key2 = createConnectQueryKey(methodDescriptor, { sentence: "" });
expect(key1).toStrictEqual(key2);
it("sets cardinality finite by default", () => {
const key = createConnectQueryKey({
schema: ElizaService.method.say,
});
expect(key[1].cardinality).toBe("finite");
});

it("allows to set cardinality: finite", () => {
const key = createConnectQueryKey({
schema: ElizaService.method.say,
cardinality: "finite",
});
expect(key[1].cardinality).toBe("finite");
});

it("allows to set cardinality: any", () => {
const key = createConnectQueryKey({
schema: ElizaService.method.say,
cardinality: "any",
});
expect(key[1].cardinality).toBeUndefined();
});

it("allows to set a service schema", () => {
const key = createConnectQueryKey({
schema: ElizaService,
});
expect(key[1].serviceName).toBe(ElizaService.typeName);
expect(key[1].methodName).toBeUndefined();
});
});
Loading

0 comments on commit 92e2997

Please sign in to comment.