Skip to content

Commit

Permalink
fix(client): a bunch of patch types #286
Browse files Browse the repository at this point in the history
  • Loading branch information
saiichihashimoto committed Sep 27, 2023
1 parent e5ba517 commit a770ce7
Show file tree
Hide file tree
Showing 3 changed files with 411 additions and 29 deletions.
138 changes: 134 additions & 4 deletions packages/client/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,10 +376,6 @@ describe("createClient", () => {
});
});

describe("patch", () => {
it.todo("https://github.com/sanity-io/client#patchupdate-a-document");
});

describe("delete", () => {
it("returns a union of the documents and an asset document", () => {
const exec = () =>
Expand Down Expand Up @@ -409,6 +405,140 @@ describe("createClient", () => {
);
});

describe("patch", () => {
it.todo("https://github.com/sanity-io/client#patchupdate-a-document");

it("returns a union of the documents", () => {
const exec = () =>
createClient<{
bar: { _type: "bar"; bar: "bar" };
foo: AnySanityDocument & { _type: "foo"; foo: string };
qux: AnySanityDocument & { _type: "qux"; qux: number };
}>()({})
.patch("id")
.commit();

expectType<ReturnType<typeof exec>>().toStrictEqual<
Promise<
| (AnySanityDocument & { _type: "foo"; foo: string })
| (AnySanityDocument & { _type: "qux"; qux: number })
>
>();
});

it("filters the union to set attrs", () => {
const exec = () =>
createClient<{
bar: { _type: "bar"; bar: "bar" };
foo: AnySanityDocument & { _type: "foo"; foo: string };
qux: AnySanityDocument & { _type: "qux"; qux: number };
}>()({})
.patch("id")
.set({ foo: "foo" })
.commit();

expectType<ReturnType<typeof exec>>().toStrictEqual<
Promise<AnySanityDocument & { _type: "foo"; foo: string }>
>();
});

it("filters the union to setIfMissing attrs", () => {
const exec = () =>
createClient<{
bar: { _type: "bar"; bar: "bar" };
foo: AnySanityDocument & { _type: "foo"; foo: string };
qux: AnySanityDocument & { _type: "qux"; qux: number };
}>()({})
.patch("id")
.setIfMissing({ foo: "foo" })
.commit();

expectType<ReturnType<typeof exec>>().toStrictEqual<
Promise<AnySanityDocument & { _type: "foo"; foo: string }>
>();
});

it.todo("diffMatchPatch");

it("filters the union to unset keys", () => {
const exec = () =>
createClient<{
bar: { _type: "bar"; bar: "bar" };
foo: AnySanityDocument & { _type: "foo"; foo: string };
qux: AnySanityDocument & { _type: "qux"; qux: number };
}>()({})
.patch("id")
.unset(["foo"])
.commit();

expectType<ReturnType<typeof exec>>().toStrictEqual<
Promise<AnySanityDocument & { _type: "foo"; foo: string }>
>();
});

it("filters the union to inc attrs", () => {
const exec = () =>
createClient<{
bar: { _type: "bar"; bar: "bar" };
foo: AnySanityDocument & { _type: "foo"; foo: number };
qux: AnySanityDocument & { _type: "qux"; qux: number };
}>()({})
.patch("id")
.inc({ foo: 1 })
.commit();

expectType<ReturnType<typeof exec>>().toStrictEqual<
Promise<AnySanityDocument & { _type: "foo"; foo: number }>
>();
});

it("filters the union to dec attrs", () => {
const exec = () =>
createClient<{
bar: { _type: "bar"; bar: "bar" };
foo: AnySanityDocument & { _type: "foo"; foo: number };
qux: AnySanityDocument & { _type: "qux"; qux: number };
}>()({})
.patch("id")
.dec({ foo: 1 })
.commit();

expectType<ReturnType<typeof exec>>().toStrictEqual<
Promise<AnySanityDocument & { _type: "foo"; foo: number }>
>();
});

it.todo("insert");

it.todo("append");

it.todo("prepend");

it.todo("splice, it does JSON paths");

it("returns original documents after reset", () => {
const exec = () =>
createClient<{
bar: { _type: "bar"; bar: "bar" };
foo: AnySanityDocument & { _type: "foo"; foo: string };
qux: AnySanityDocument & { _type: "qux"; qux: number };
}>()({})
.patch("id")
.set({ foo: "foo" })
.reset()
.commit();

expectType<ReturnType<typeof exec>>().toStrictEqual<
Promise<
| (AnySanityDocument & { _type: "foo"; foo: string })
| (AnySanityDocument & { _type: "qux"; qux: number })
>
>();
});

it.todo("try all of them with deep and JSON Paths");
});

describe("transaction", () => {
it.todo(
"https://github.com/sanity-io/client#multiple-mutations-in-a-transaction"
Expand Down
164 changes: 143 additions & 21 deletions packages/client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import type {
MutationEvent as MutationEventNative,
MutationSelection,
ObservableSanityClient as ObservableSanityClientNative,
Patch as PatchNative,
PatchOperations,
PatchSelection,
QueryParams,
RawQueryResponse as RawQueryResponseNative,
SanityAssetDocument,
Expand All @@ -27,6 +30,116 @@ declare const README: unique symbol;

type AnySanityDocument = Omit<SanityDocument, "_type"> & { _type: string };

type PromiseOrObservable<
TIsPromise extends boolean,
T
> = TIsPromise extends true ? Promise<T> : Observable<T>;

type Patch<
TDocument extends AnySanityDocument,
TOriginalDocument extends AnySanityDocument,
TIsPromise extends boolean
> = Omit<
PatchNative,
| "append"
| "clone"
| "commit"
| "dec"
| "diffMatchPatch"
| "ifRevisionId"
| "inc"
| "insert"
| "prepend"
| "reset"
| "set"
| "setIfMissing"
| "splice"
| "unset"
> & {
append: (
...args: Parameters<PatchNative["append"]>
) => Patch<TDocument, TOriginalDocument, TIsPromise>;
clone: (
...args: Parameters<PatchNative["clone"]>
) => Patch<TDocument, TOriginalDocument, TIsPromise>;
commit: <
const TOptions extends
| (BaseMutationOptions & {
returnDocuments?: boolean;
returnFirst?: boolean;
})
| undefined = undefined
>(
options?: TOptions
) => PromiseOrObservable<
TIsPromise,
TOptions extends { returnDocuments: false; returnFirst: false }
? MultipleMutationResult
: TOptions extends { returnFirst: false }
? TDocument[]
: TOptions extends { returnDocuments: false }
? SingleMutationResult
: TDocument
>;
dec: <TAttrs extends Partial<TDocument & { [key: string]: number }>>(
attrs: TAttrs
) => Patch<
Extract<TDocument, Partial<TAttrs>>,
TOriginalDocument,
TIsPromise
>;
diffMatchPatch: (
...args: Parameters<PatchNative["diffMatchPatch"]>
) => Patch<TDocument, TOriginalDocument, TIsPromise>;
ifRevisionId: (
...args: Parameters<PatchNative["ifRevisionId"]>
) => Patch<TDocument, TOriginalDocument, TIsPromise>;
inc: <TAttrs extends Partial<TDocument & { [key: string]: number }>>(
attrs: TAttrs
) => Patch<
Extract<TDocument, Partial<TAttrs>>,
TOriginalDocument,
TIsPromise
>;
insert: (
...args: Parameters<PatchNative["insert"]>
) => Patch<TDocument, TOriginalDocument, TIsPromise>;
prepend: (
...args: Parameters<PatchNative["prepend"]>
) => Patch<TDocument, TOriginalDocument, TIsPromise>;
reset: (
...args: Parameters<PatchNative["reset"]>
) => Patch<TOriginalDocument, TOriginalDocument, TIsPromise>;
set: <TAttrs extends Partial<TDocument>>(
attrs: TAttrs
) => Patch<
Extract<TDocument, Partial<TAttrs>>,
TOriginalDocument,
TIsPromise
>;
setIfMissing: <TAttrs extends Partial<TDocument>>(
attrs: TAttrs
) => Patch<
Extract<TDocument, Partial<TAttrs>>,
TOriginalDocument,
TIsPromise
>;
splice: (
...args: Parameters<PatchNative["splice"]>
) => Patch<TDocument, TOriginalDocument, TIsPromise>;
unset: <TAttrs extends TDocument extends never ? never : (keyof TDocument)[]>(
attrs: TAttrs
) => Patch<
TDocument extends never
? never
: TAttrs[number] extends keyof TDocument
? TDocument
: never,
TOriginalDocument,
TIsPromise
>;
};

export type InitializedClientConfig<TClientConfig extends ClientConfig> = Merge<
InitializedClientConfigNative,
TClientConfig
Expand Down Expand Up @@ -66,11 +179,6 @@ type GetDocuments<TDocument extends AnySanityDocument, Ids extends string[]> = {
[index in keyof Ids]: (TDocument & { _id: Ids[index] }) | null;
};

type PromiseOrObservable<
TIsPromise extends boolean,
T
> = TIsPromise extends true ? Promise<T> : Observable<T>;

type OverrideSanityClient<
TSanityClient,
TClientConfig extends ClientConfig,
Expand All @@ -88,6 +196,7 @@ type OverrideSanityClient<
| "getDocument"
| "getDocuments"
| "observable"
| "patch"
| "withConfig"
> & {
clone: () => OverrideSanityClient<
Expand All @@ -113,10 +222,12 @@ type OverrideSanityClient<
SetOptional<TDocument, "_id">,
"_createdAt" | "_rev" | "_updatedAt"
> & { _type: string },
const TOptions extends BaseMutationOptions & {
returnDocuments?: boolean;
returnFirst?: boolean;
}
const TOptions extends
| (BaseMutationOptions & {
returnDocuments?: boolean;
returnFirst?: boolean;
})
| undefined = undefined
>(
document: Doc,
options?: TOptions
Expand All @@ -134,10 +245,12 @@ type OverrideSanityClient<
const Doc extends Omit<TDocument, "_createdAt" | "_rev" | "_updatedAt"> & {
_type: string;
},
const TOptions extends BaseMutationOptions & {
returnDocuments?: boolean;
returnFirst?: boolean;
}
const TOptions extends
| (BaseMutationOptions & {
returnDocuments?: boolean;
returnFirst?: boolean;
})
| undefined = undefined
>(
document: Doc,
options?: TOptions
Expand All @@ -155,10 +268,12 @@ type OverrideSanityClient<
const Doc extends Omit<TDocument, "_createdAt" | "_rev" | "_updatedAt"> & {
_type: string;
},
const TOptions extends BaseMutationOptions & {
returnDocuments?: boolean;
returnFirst?: boolean;
}
const TOptions extends
| (BaseMutationOptions & {
returnDocuments?: boolean;
returnFirst?: boolean;
})
| undefined = undefined
>(
document: Doc,
options?: TOptions
Expand All @@ -173,10 +288,12 @@ type OverrideSanityClient<
: Extract<TDocument, { _type: Doc["_type"] }>
>;
delete: <
const TOptions extends BaseMutationOptions & {
returnDocuments?: boolean;
returnFirst?: boolean;
}
const TOptions extends
| (BaseMutationOptions & {
returnDocuments?: boolean;
returnFirst?: boolean;
})
| undefined = undefined
>(
idOrSelection: MutationSelection | string,
options?: TOptions
Expand Down Expand Up @@ -270,6 +387,11 @@ type OverrideSanityClient<
>;
observable: // eslint-disable-next-line @typescript-eslint/no-use-before-define -- Recursive type
ObservableSanityClient<TClientConfig, TDocument>;
patch: (
idOrSelection: PatchSelection,
// TODO PatchOperations should filter like Patch fns do
options?: PatchOperations
) => Patch<TDocument, TDocument, TIsPromise>;
withConfig: <const NewConfig extends Partial<ClientConfig>>(
newConfig?: NewConfig
) => OverrideSanityClient<
Expand Down

0 comments on commit a770ce7

Please sign in to comment.