Skip to content
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

V2: Update query keys for partial matching #441

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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