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

Commit

Permalink
Wrap path params with url encoding function
Browse files Browse the repository at this point in the history
  • Loading branch information
brotheroftux authored and fabien0102 committed Sep 1, 2020
1 parent 11f7b73 commit 0bd6866
Show file tree
Hide file tree
Showing 15 changed files with 370 additions and 136 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "restful-react",
"version": "14.4.0",
"version": "14.5.0",
"description": "A consistent, declarative way of interacting with RESTful backends, featuring code-generation from Swagger and OpenAPI specs",
"keywords": [
"rest",
Expand Down
4 changes: 2 additions & 2 deletions src/Context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ export default class RestfulReactProvider<T> extends React.Component<RestfulReac
resolve: (data: any) => data,
requestOptions: {},
parentPath: "",
queryParams: value.queryParams || {},
queryParamStringifyOptions: value.queryParamStringifyOptions || {},
queryParams: {},
queryParamStringifyOptions: {},
...value,
}}
>
Expand Down
21 changes: 15 additions & 6 deletions src/Get.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ describe("Get", () => {

const { rerender } = render(
<RestfulProvider base="https://my-awesome-api.fake">
<Get path="?test=1" debounce>
<Get path="" queryParams={{ test: 1 }} debounce>
{children}
</Get>
</RestfulProvider>,
Expand All @@ -699,12 +699,13 @@ describe("Get", () => {
times(10, i =>
rerender(
<RestfulProvider base="https://my-awesome-api.fake">
<Get path={`?test=${i + 1}`} debounce>
<Get path="" queryParams={{ test: i + 1 }} debounce>
{children}
</Get>
</RestfulProvider>,
),
);

await wait(() => expect(apiCalls).toEqual(1));
});

Expand All @@ -721,14 +722,18 @@ describe("Get", () => {

const { rerender } = render(
<RestfulProvider base="https://my-awesome-api.fake">
<Get path="?test=1">{children}</Get>
<Get path="" queryParams={{ test: 1 }}>
{children}
</Get>
</RestfulProvider>,
);

times(10, i =>
rerender(
<RestfulProvider base="https://my-awesome-api.fake">
<Get path={`?test=${i + 1}`}>{children}</Get>
<Get path="" queryParams={{ test: i + 1 }}>
{children}
</Get>
</RestfulProvider>,
),
);
Expand Down Expand Up @@ -946,13 +951,17 @@ describe("Get", () => {

const { rerender } = render(
<RestfulProvider base="https://my-awesome-api.fake">
<Get path="/?test=0">{children}</Get>
<Get path="" queryParams={{ test: 0 }}>
{children}
</Get>
</RestfulProvider>,
);

rerender(
<RestfulProvider base="https://my-awesome-api.fake">
<Get path="/?test=1">{children}</Get>
<Get path="" queryParams={{ test: 1 }}>
{children}
</Get>
</RestfulProvider>,
);

Expand Down
27 changes: 15 additions & 12 deletions src/Get.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { DebounceSettings } from "lodash";
import debounce from "lodash/debounce";
import isEqual from "lodash/isEqual";
import * as qs from "qs";
import * as React from "react";

import RestfulReactProvider, { InjectedProps, RestfulReactConsumer, RestfulReactProviderProps } from "./Context";
import { composePath, composeUrl } from "./util/composeUrl";
import { processResponse } from "./util/processResponse";
import { resolveData } from "./util/resolveData";
import { constructUrl } from "./util/constructUrl";
import { IStringifyOptions } from "qs";

/**
* A function that resolves returned data from
Expand Down Expand Up @@ -86,6 +87,10 @@ export interface GetProps<TData, TError, TQueryParams, TPathParams> {
* Query parameters
*/
queryParams?: TQueryParams;
/**
* Query parameter stringify options
*/
queryParamStringifyOptions?: IStringifyOptions;
/**
* Don't send the error to the Provider
*/
Expand Down Expand Up @@ -250,18 +255,12 @@ class ContextlessGet<TData, TError, TQueryParams, TPathParams = unknown> extends
}

const makeRequestPath = () => {
let url: string;
if (__internal_hasExplicitBase) {
url = composeUrl(base!, "", path || "");
} else {
url = composeUrl(base!, parentPath!, requestPath || path || "");
}
const concatPath = __internal_hasExplicitBase ? path : composePath(parentPath, path);

// We use ! because it's in defaultProps
if (Object.keys(this.props.queryParams!).length) {
url += `?${qs.stringify(this.props.queryParams)}`;
}
return url;
return constructUrl(base!, concatPath, this.props.queryParams, {
stripTrailingSlash: true,
queryParamOptions: this.props.queryParamStringifyOptions,
});
};

const request = new Request(makeRequestPath(), await this.getRequestOptions(makeRequestPath(), thisRequestOptions));
Expand Down Expand Up @@ -355,6 +354,10 @@ function Get<TData = any, TError = any, TQueryParams = { [key: string]: any }, T
{...props}
queryParams={{ ...contextProps.queryParams, ...props.queryParams }}
__internal_hasExplicitBase={Boolean(props.base)}
queryParamStringifyOptions={{
...contextProps.queryParamStringifyOptions,
...props.queryParamStringifyOptions,
}}
/>
</RestfulReactProvider>
)}
Expand Down
46 changes: 28 additions & 18 deletions src/Mutate.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import * as qs from "qs";
import * as React from "react";
import RestfulReactProvider, { InjectedProps, RestfulReactConsumer, RestfulReactProviderProps } from "./Context";
import { GetState } from "./Get";
import { composePath, composeUrl } from "./util/composeUrl";
import { processResponse } from "./util/processResponse";
import { constructUrl } from "./util/constructUrl";
import { IStringifyOptions } from "qs";

/**
* An enumeration of states that a fetchable
Expand Down Expand Up @@ -63,6 +64,10 @@ export interface MutateProps<TData, TError, TQueryParams, TRequestBody, TPathPar
* Query parameters
*/
queryParams?: TQueryParams;
/**
* Query parameter stringify options
*/
queryParamStringifyOptions?: IStringifyOptions;
/**
* An escape hatch and an alternative to `path` when you'd like
* to fetch from an entirely different URL.
Expand Down Expand Up @@ -98,6 +103,11 @@ export interface MutateProps<TData, TError, TQueryParams, TRequestBody, TPathPar
* @param data - Response data
*/
onMutate?: (body: TRequestBody, data: TData) => void;
/**
* A function to encode body of DELETE requests when appending it
* to an existing path
*/
pathInlineBodyEncode?: typeof encodeURIComponent;
}

/**
Expand Down Expand Up @@ -155,28 +165,24 @@ class ContextlessMutate<TData, TError, TQueryParams, TRequestBody, TPathParams>
onError,
onRequest,
onResponse,
pathInlineBodyEncode,
} = this.props;
this.setState(() => ({ error: null, loading: true }));

const makeRequestPath = () => {
let url: string;
if (__internal_hasExplicitBase) {
url =
verb === "DELETE" && typeof body === "string"
? composeUrl(base!, "", composePath(path!, body))
: composeUrl(base!, "", path || "");
} else {
url =
verb === "DELETE" && typeof body === "string"
? composeUrl(base!, parentPath!, composePath(path!, body))
: composeUrl(base!, parentPath!, path!);
}
const pathWithPossibleBody =
verb === "DELETE" && typeof body === "string"
? composePath(path, pathInlineBodyEncode ? pathInlineBodyEncode(body) : body)
: path;

// We use ! because it's in defaultProps
if (Object.keys(this.props.queryParams!).length) {
url += `?${qs.stringify({ ...this.props.queryParams, ...mutateRequestOptions?.queryParams })}`;
}
return url;
const concatPath = __internal_hasExplicitBase
? pathWithPossibleBody || ""
: composePath(parentPath, pathWithPossibleBody);

return constructUrl(base!, concatPath, this.props.queryParams, {
stripTrailingSlash: true,
queryParamOptions: this.props.queryParamStringifyOptions,
});
};

const request = new Request(makeRequestPath(), {
Expand Down Expand Up @@ -285,6 +291,10 @@ function Mutate<
{...contextProps}
{...props}
queryParams={{ ...contextProps.queryParams, ...props.queryParams } as TQueryParams}
queryParamStringifyOptions={{
...contextProps.queryParamStringifyOptions,
...props.queryParamStringifyOptions,
}}
__internal_hasExplicitBase={Boolean(props.base)}
/>
</RestfulReactProvider>
Expand Down
23 changes: 15 additions & 8 deletions src/Poll.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import merge from "lodash/merge";
import * as qs from "qs";
import * as React from "react";
import equal from "react-fast-compare";

import { InjectedProps, RestfulReactConsumer } from "./Context";
import { GetProps, GetState, Meta as GetComponentMeta } from "./Get";
import { composeUrl } from "./util/composeUrl";
import { processResponse } from "./util/processResponse";
import { constructUrl } from "./util/constructUrl";
import { IStringifyOptions } from "qs";

/**
* Meta information returned from the poll.
Expand Down Expand Up @@ -106,6 +107,10 @@ export interface PollProps<TData, TError, TQueryParams, TPathParams> {
* Query parameters
*/
queryParams?: TQueryParams;
/**
* Query parameter stringify options
*/
queryParamStringifyOptions?: IStringifyOptions;
/**
* Don't send the error to the Provider
*/
Expand Down Expand Up @@ -214,20 +219,18 @@ class ContextlessPoll<TData, TError, TQueryParams, TPathParams = unknown> extend

// Should we stop?
if (this.props.until && this.props.until(this.state.data, this.state.lastResponse)) {
await this.stop(); // stop.
this.stop(); // stop.
return;
}

// If we should keep going,
const { base, path, interval, wait, onError, onRequest, onResponse } = this.props;
const { lastPollIndex } = this.state;

let url = composeUrl(base!, "", path);

// We use a ! because it's in defaultProps
if (Object.keys(this.props.queryParams!).length) {
url += `?${qs.stringify(this.props.queryParams)}`;
}
const url = constructUrl(base!, path, this.props.queryParams, {
queryParamOptions: this.props.queryParamStringifyOptions,
stripTrailingSlash: true,
});

const requestOptions = await this.getRequestOptions(url);

Expand Down Expand Up @@ -366,6 +369,10 @@ function Poll<TData = any, TError = any, TQueryParams = { [key: string]: any },

return merge(contextRequestOptions, propsRequestOptions);
}}
queryParamStringifyOptions={{
...contextProps.queryParamStringifyOptions,
...props.queryParamStringifyOptions,
}}
/>
);
}}
Expand Down
6 changes: 6 additions & 0 deletions src/bin/restful-react-import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ export type AdvancedOptions = Options & {
customProps?: {
base?: string;
};

pathParametersEncodingMode?: "uriComponent" | "rfc3986";

customGenerator?: (data: {
componentName: string;
verb: string;
Expand Down Expand Up @@ -81,6 +84,7 @@ const importSpecs = async (options: AdvancedOptions) => {
customImport: options.customImport,
customProps: options.customProps,
customGenerator: options.customGenerator,
pathParametersEncodingMode: options.pathParametersEncodingMode,
});
} else if (options.url) {
const { url } = options;
Expand Down Expand Up @@ -116,6 +120,7 @@ const importSpecs = async (options: AdvancedOptions) => {
customImport: options.customImport,
customProps: options.customProps,
customGenerator: options.customGenerator,
pathParametersEncodingMode: options.pathParametersEncodingMode,
}),
);
});
Expand Down Expand Up @@ -203,6 +208,7 @@ const importSpecs = async (options: AdvancedOptions) => {
customImport: options.customImport,
customProps: options.customProps,
customGenerator: options.customGenerator,
pathParametersEncodingMode: options.pathParametersEncodingMode,
}),
);
});
Expand Down
Loading

0 comments on commit 0bd6866

Please sign in to comment.