Skip to content
This repository has been archived by the owner on Nov 11, 2023. It is now read-only.

Commit

Permalink
Add support for global queryParams and composition
Browse files Browse the repository at this point in the history
  • Loading branch information
Tejas Kumar committed Oct 22, 2019
1 parent 7d885b3 commit 80bac52
Show file tree
Hide file tree
Showing 11 changed files with 461 additions and 11 deletions.
7 changes: 7 additions & 0 deletions src/Context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ export interface RestfulReactProviderProps<T = any> {
* to deal with your retry locally instead of in the provider scope.
*/
onError?: (err: any, retry: () => Promise<T | null>, response?: Response) => void;
/**
* Any global level query params?
* **Warning:** it's probably not a good idea to put API keys here. Consider headers instead.
*/
queryParams?: { [key: string]: any };
}

export const Context = React.createContext<Required<RestfulReactProviderProps>>({
Expand All @@ -35,6 +40,7 @@ export const Context = React.createContext<Required<RestfulReactProviderProps>>(
resolve: (data: any) => data,
requestOptions: {},
onError: noop,
queryParams: {},
});

export interface InjectedProps {
Expand All @@ -51,6 +57,7 @@ export default class RestfulReactProvider<T> extends React.Component<RestfulReac
resolve: (data: any) => data,
requestOptions: {},
parentPath: "",
queryParams: value.queryParams || {},
...value,
}}
>
Expand Down
62 changes: 62 additions & 0 deletions src/Get.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,68 @@ describe("Get", () => {
</RestfulProvider>,
);

await wait(() => expect(children.mock.calls.length).toBe(2));
});
it("should inherit provider's queryParams if none specified", async () => {
nock("https://my-awesome-api.fake")
.get("/")
.query({
myParam: true,
})
.reply(200);

const children = jest.fn();
children.mockReturnValue(<div />);

render(
<RestfulProvider queryParams={{ myParam: true }} base="https://my-awesome-api.fake">
<Get<void, void, { myParam: boolean }> path="">{children}</Get>
</RestfulProvider>,
);

await wait(() => expect(children.mock.calls.length).toBe(2));
});
it("should override provider's queryParams if own specified", async () => {
nock("https://my-awesome-api.fake")
.get("/")
.query({
myParam: false,
})
.reply(200);

const children = jest.fn();
children.mockReturnValue(<div />);

render(
<RestfulProvider queryParams={{ myParam: true }} base="https://my-awesome-api.fake">
<Get<void, void, { myParam: boolean }> path="" queryParams={{ myParam: false }}>
{children}
</Get>
</RestfulProvider>,
);

await wait(() => expect(children.mock.calls.length).toBe(2));
});
it("should merge provider's queryParams with own", async () => {
nock("https://my-awesome-api.fake")
.get("/")
.query({
myParam: false,
otherParam: true,
})
.reply(200);

const children = jest.fn();
children.mockReturnValue(<div />);

render(
<RestfulProvider queryParams={{ otherParam: true }} base="https://my-awesome-api.fake">
<Get<void, void, { myParam: boolean }> path="" queryParams={{ myParam: false }}>
{children}
</Get>
</RestfulProvider>,
);

await wait(() => expect(children.mock.calls.length).toBe(2));
});
});
Expand Down
10 changes: 8 additions & 2 deletions src/Get.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ class ContextlessGet<TData, TError, TQueryParams> extends React.Component<
base: "",
parentPath: "",
resolve: (unresolvedData: any) => unresolvedData,
queryParams: {},
};

public componentDidMount() {
Expand Down Expand Up @@ -245,7 +246,7 @@ class ContextlessGet<TData, TError, TQueryParams> extends React.Component<
} else {
url = composeUrl(base!, parentPath!, requestPath || path || "");
}
if (this.props.queryParams) {
if (Object.keys(this.props.queryParams).length) {
url += `?${qs.stringify(this.props.queryParams)}`;
}
return url;
Expand Down Expand Up @@ -335,7 +336,12 @@ function Get<TData = any, TError = any, TQueryParams = { [key: string]: any }>(
<RestfulReactConsumer>
{contextProps => (
<RestfulReactProvider {...contextProps} parentPath={composePath(contextProps.parentPath, props.path)}>
<ContextlessGet {...contextProps} {...props} __internal_hasExplicitBase={Boolean(props.base)} />
<ContextlessGet
{...contextProps}
{...props}
queryParams={{ ...contextProps.queryParams, ...props.queryParams }}
__internal_hasExplicitBase={Boolean(props.base)}
/>
</RestfulReactProvider>
)}
</RestfulReactConsumer>
Expand Down
105 changes: 104 additions & 1 deletion src/Mutate.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ describe("Mutate", () => {
expect(children.mock.calls[2][1].loading).toEqual(false);
});
});
describe("Compose paths and urls", () => {
describe("Compose paths, urls, and query parameters", () => {
it("should compose absolute urls", async () => {
nock("https://my-awesome-api.fake")
.post("/absolute")
Expand Down Expand Up @@ -796,6 +796,109 @@ describe("Mutate", () => {
// transition state
expect(children.mock.calls[1][1].loading).toEqual(true);

// after post state
expect(children.mock.calls[2][1].loading).toEqual(false);
});
it("should inherit provider's query params if present", async () => {
nock("https://my-awesome-api.fake")
.post("/")
.query({
myParam: true,
})
.reply(200, { id: 1 });

const children = jest.fn();
children.mockReturnValue(<div />);

// setup - first render
render(
<RestfulProvider base="https://my-awesome-api.fake" queryParams={{ myParam: true }}>
<Mutate<void, void, { myParam: boolean }> verb="POST" path="">
{children}
</Mutate>
</RestfulProvider>,
);

await wait(() => expect(children.mock.calls.length).toBe(1));
expect(children.mock.calls[0][1].loading).toEqual(false);
expect(children.mock.calls[0][0]).toBeDefined();

// post action
children.mock.calls[0][0]();
await wait(() => expect(children.mock.calls.length).toBe(3));

// transition state
expect(children.mock.calls[1][1].loading).toEqual(true);

// after post state
expect(children.mock.calls[2][1].loading).toEqual(false);
});
it("should override provider's query params if own present", async () => {
nock("https://my-awesome-api.fake")
.post("/")
.query({
myParam: false,
})
.reply(200, { id: 1 });

const children = jest.fn();
children.mockReturnValue(<div />);

// setup - first render
render(
<RestfulProvider base="https://my-awesome-api.fake" queryParams={{ myParam: true }}>
<Mutate<void, void, { myParam: boolean }> verb="POST" path="" queryParams={{ myParam: false }}>
{children}
</Mutate>
</RestfulProvider>,
);

await wait(() => expect(children.mock.calls.length).toBe(1));
expect(children.mock.calls[0][1].loading).toEqual(false);
expect(children.mock.calls[0][0]).toBeDefined();

// post action
children.mock.calls[0][0]();
await wait(() => expect(children.mock.calls.length).toBe(3));

// transition state
expect(children.mock.calls[1][1].loading).toEqual(true);

// after post state
expect(children.mock.calls[2][1].loading).toEqual(false);
});
it("should merge provider's query params with own if present", async () => {
nock("https://my-awesome-api.fake")
.post("/")
.query({
myParam: false,
otherParam: true,
})
.reply(200, { id: 1 });

const children = jest.fn();
children.mockReturnValue(<div />);

// setup - first render
render(
<RestfulProvider base="https://my-awesome-api.fake" queryParams={{ otherParam: true }}>
<Mutate<void, void, { myParam: boolean }> verb="POST" path="" queryParams={{ myParam: false }}>
{children}
</Mutate>
</RestfulProvider>,
);

await wait(() => expect(children.mock.calls.length).toBe(1));
expect(children.mock.calls[0][1].loading).toEqual(false);
expect(children.mock.calls[0][0]).toBeDefined();

// post action
children.mock.calls[0][0]();
await wait(() => expect(children.mock.calls.length).toBe(3));

// transition state
expect(children.mock.calls[1][1].loading).toEqual(true);

// after post state
expect(children.mock.calls[2][1].loading).toEqual(false);
});
Expand Down
4 changes: 3 additions & 1 deletion src/Mutate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ class ContextlessMutate<TData, TError, TQueryParams, TRequestBody> extends React
base: "",
parentPath: "",
path: "",
queryParams: {},
};

/**
Expand Down Expand Up @@ -149,7 +150,7 @@ class ContextlessMutate<TData, TError, TQueryParams, TRequestBody> extends React
? composeUrl(base!, parentPath!, composePath(path!, body))
: composeUrl(base!, parentPath!, path!);
}
if (this.props.queryParams) {
if (Object.keys(this.props.queryParams).length) {
url += `?${qs.stringify(this.props.queryParams)}`;
}
return url;
Expand Down Expand Up @@ -252,6 +253,7 @@ function Mutate<TData = any, TError = any, TQueryParams = { [key: string]: any }
<ContextlessMutate<TData, TError, TQueryParams, TRequestBody>
{...contextProps}
{...props}
queryParams={{ ...contextProps.queryParams, ...props.queryParams }}
__internal_hasExplicitBase={Boolean(props.base)}
/>
</RestfulReactProvider>
Expand Down
107 changes: 107 additions & 0 deletions src/Poll.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,113 @@ describe("Poll", () => {
</RestfulProvider>,
);

await wait(() => expect(children.mock.calls.length).toBe(2));
});
it("should inherit query parameters from provider if none specified", async () => {
nock("https://my-awesome-api.fake", {
reqheaders: {
prefer: "wait=60s;",
},
})
.get("/")
.query({
myParam: true,
})
.reply(200, { data: "hello" }, { "x-polling-index": "1" });

nock("https://my-awesome-api.fake", {
reqheaders: {
prefer: "wait=60s;index=1",
},
})
.get("/")
.query({
myParam: true,
})
.reply(200, { data: "hello" }, { "x-polling-index": "2" });

const children = jest.fn();
children.mockReturnValue(<div />);

render(
<RestfulProvider base="https://my-awesome-api.fake" queryParams={{ myParam: true }}>
<Poll<void, void, { myParam: boolean }> path="">{children}</Poll>
</RestfulProvider>,
);

await wait(() => expect(children.mock.calls.length).toBe(2));
});
it("should override query parameters from provider if own specified", async () => {
nock("https://my-awesome-api.fake", {
reqheaders: {
prefer: "wait=60s;",
},
})
.get("/")
.query({
myParam: false,
})
.reply(200, { data: "hello" }, { "x-polling-index": "1" });

nock("https://my-awesome-api.fake", {
reqheaders: {
prefer: "wait=60s;index=1",
},
})
.get("/")
.query({
myParam: true,
})
.reply(200, { data: "hello" }, { "x-polling-index": "2" });

const children = jest.fn();
children.mockReturnValue(<div />);

render(
<RestfulProvider base="https://my-awesome-api.fake" queryParams={{ myParam: true }}>
<Poll<void, void, { myParam: boolean }> path="" queryParams={{ myParam: false }}>
{children}
</Poll>
</RestfulProvider>,
);

await wait(() => expect(children.mock.calls.length).toBe(2));
});
it("should merge query parameters from provider when both specified", async () => {
nock("https://my-awesome-api.fake", {
reqheaders: {
prefer: "wait=60s;",
},
})
.get("/")
.query({
myParam: false,
otherParam: true,
})
.reply(200, { data: "hello" }, { "x-polling-index": "1" });

nock("https://my-awesome-api.fake", {
reqheaders: {
prefer: "wait=60s;index=1",
},
})
.get("/")
.query({
myParam: true,
})
.reply(200, { data: "hello" }, { "x-polling-index": "2" });

const children = jest.fn();
children.mockReturnValue(<div />);

render(
<RestfulProvider base="https://my-awesome-api.fake" queryParams={{ otherParam: true }}>
<Poll<void, void, { myParam: boolean }> path="" queryParams={{ myParam: false }}>
{children}
</Poll>
</RestfulProvider>,
);

await wait(() => expect(children.mock.calls.length).toBe(2));
});
});
Expand Down
Loading

0 comments on commit 80bac52

Please sign in to comment.