Skip to content

Commit

Permalink
feat: add makeAxios (#248)
Browse files Browse the repository at this point in the history
* feat: add makeAxios

* add documentation
  • Loading branch information
pikax committed Apr 20, 2020
1 parent 89197d5 commit 4ae8cc5
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 152 deletions.
5 changes: 4 additions & 1 deletion docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,10 @@ module.exports = {
title: "External",
sidebarDepth: 1,
collapsable: false,
children: [["composable/external/axios", "axios"]]
children: [
["composable/external/axios", "axios"],
["composable/external/makeAxios", "makeAxios"]
]
}
]
},
Expand Down
31 changes: 31 additions & 0 deletions docs/api/axios.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,37 @@ import { AxiosResponse } from "axios";
import { PromiseResultFactory } from "vue-composable";
import { Ref } from "@vue/composition-api";

// @public (undocumented)
export function makeAxios(
client: AxiosInstance,
throwException?: boolean
): {
client: AxiosInstance;
data: Readonly<
import("@vue/composition-api/dist/reactivity/ref").Ref<Readonly<any>>
>;
status: Readonly<
import("@vue/composition-api/dist/reactivity/ref").Ref<number>
>;
statusText: Readonly<
import("@vue/composition-api/dist/reactivity/ref").Ref<string>
>;
cancel: (message?: string | undefined) => void;
isCancelled: import("@vue/composition-api/dist/reactivity/ref").Ref<boolean>;
cancelledMessage: import("@vue/composition-api/dist/reactivity/ref").Ref<
string | null | undefined
>;
exec: (
request: string | AxiosRequestConfig
) => Promise<AxiosResponse<any> | undefined>;
promise: import("@vue/reactivity").Ref<
Promise<AxiosResponse<any>> | undefined
>;
result: import("@vue/reactivity").Ref<AxiosResponse<any> | null>;
loading: import("@vue/reactivity").Ref<boolean>;
error: import("@vue/reactivity").Ref<any>;
};

// Warning: (ae-forgotten-export) The symbol "AxiosReturn" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
Expand Down
95 changes: 95 additions & 0 deletions docs/composable/external/makeAxios.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# makeAxios

> Wraps [axios](https://github.com/axios/axios) instance.
## Installing

```bash
# install with yarn
yarn add @vue-composable/axios

# install with npm
npm install @vue-composable/axios
```

## Parameters

```js
import { makeAxios } from "@vue-composable/axios";

makeAxios(client, throwException?);

```
| Parameters | Type | Required | Default | Description |
| -------------- | --------------- | -------- | ----------- | ---------------------------------------------------------------------------------------------- |
| client | `AxiosInstance` | `true` | `undefined` | Uses this client |
| throwException | `Boolean` | `false` | `false` | Makes `exec` throw exceptions, when `false` the error will be handled only by the `usePromise` |
## State
The `makeAxios` function exposes the following reactive state:
```js
import { makeAxios } from "@vue-composable/axios";

const {
client,
data,
status,
statusText,

// cancel
isCancelled,
cancelledMessage,

// promise
promise,
result,
loading,
error
} = makeAxios();
```
| State | Type | Description |
| ---------------- | ------------- | -------------------------------------------------------------------- |
| client | `AxiosClient` | Axios client used |
| data | `any` | Axios `response.data` |
| status | `Number` | Axios `response.status` |
| statusText | `String` | Axios `response.statusText` |
| isCancelled | `Boolean` | If the request has been cancelled by the user (executing `cancel()`) |
| cancelledMessage | `String` | Message provided when cancelling the request |
| promise | `Promise` | Current promise |
| result | `any` | Resolved value |
| loading | `boolean` | Waiting for the promise to be resolved |
| error | `any` | Promise error |
## Methods
The `makeAxios` function exposes the following methods:
```js
import { makeAxios } from "@vue-composable/axios";

const { exec, cancel } = makeAxios();
```
| Signature | Description |
| --------------------------- | ------------------------ |
| `exec(AxiosRequest|string)` | Executes axios request |
| `cancel(message?)` | Cancels the last request |
::: tip
You can pass `throwException` on the last argument of the `exec` to override the default behaviour
:::
### Code
```ts
const client = axios.create(config);

const { data, error, exec } = makeAxios(client);

exec(request);
exec(url);
```
2 changes: 2 additions & 0 deletions packages/axios/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* istanbul ignore file */

export {
ref,
isRef,
Expand Down
80 changes: 80 additions & 0 deletions packages/axios/src/axios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { computed, Ref, ComputedRef } from "./api";
import axios, { AxiosRequestConfig, AxiosResponse, AxiosInstance } from "axios";
import {
PromiseResultFactory,
isString,
isBoolean,
isObject
} from "vue-composable";
import { makeAxios } from "./makeAxios";

interface AxiosReturn<TData>
extends PromiseResultFactory<
Promise<AxiosResponse<TData>>,
[AxiosRequestConfig | string]
> {
readonly client: Ref<Readonly<AxiosInstance>>;
readonly data: ComputedRef<TData | null>;
readonly status: Ref<number | null>;
readonly statusText: Ref<string | null>;

cancel: (message?: string) => void;
readonly isCancelled: Ref<boolean>;
readonly cancelledMessage: Ref<string | null | undefined>;
// readonly
}

export function useAxios<TData = any>(
throwException?: boolean
): AxiosReturn<TData>;
export function useAxios<TData = any>(
url: string,
config?: AxiosRequestConfig,
throwException?: boolean
): AxiosReturn<TData>;
export function useAxios<TData = any>(
url: string,
throwException?: boolean
): AxiosReturn<TData>;
export function useAxios<TData = any>(
config?: AxiosRequestConfig,
throwException?: boolean
): AxiosReturn<TData>;
export function useAxios<TData = any>(
configUrlThrowException?: AxiosRequestConfig | string | boolean,
configThrowException?: AxiosRequestConfig | boolean,
throwException = false
): AxiosReturn<TData> {
/* istanbul ignore next */
__DEV__ && !axios && console.warn(`[axios] not installed, please install it`);

const config =
!isString(configUrlThrowException) && !isBoolean(configUrlThrowException)
? configUrlThrowException
: isObject(configThrowException)
? (configThrowException as AxiosRequestConfig)
: undefined;
throwException = isBoolean(configUrlThrowException)
? configUrlThrowException
: isBoolean(configThrowException)
? configThrowException
: throwException;

const axiosClient = axios.create(config);
const client = computed(() => axiosClient);

const use = makeAxios(axiosClient, throwException);

// if url provided in the config, execute it straight away
// NOTE: `false` is passed to the `exec` to prevent the exception to be thrown
if (typeof configUrlThrowException === "string") {
(use.exec as any)({ ...config, url: configUrlThrowException }, false);
} else if (config && config.url) {
(use.exec as any)(config, false);
}

return {
...use,
client
};
}
153 changes: 2 additions & 151 deletions packages/axios/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,151 +1,2 @@
import { computed, Ref, ComputedRef, ref } from "./api";
import axios, {
AxiosRequestConfig,
AxiosResponse,
AxiosInstance,
CancelTokenSource
} from "axios";
import {
usePromise,
PromiseResultFactory,
isString,
isBoolean,
isObject
} from "vue-composable";

interface AxiosReturn<TData>
extends PromiseResultFactory<
Promise<AxiosResponse<TData>>,
[AxiosRequestConfig | string]
> {
readonly client: Ref<Readonly<AxiosInstance>>;
readonly data: ComputedRef<TData | null>;
readonly status: Ref<number | null>;
readonly statusText: Ref<string | null>;

cancel: (message?: string) => void;
readonly isCancelled: Ref<boolean>;
readonly cancelledMessage: Ref<string | null | undefined>;
// readonly
}

export function useAxios<TData = any>(
throwException?: boolean
): AxiosReturn<TData>;
export function useAxios<TData = any>(
url: string,
config?: AxiosRequestConfig,
throwException?: boolean
): AxiosReturn<TData>;
export function useAxios<TData = any>(
url: string,
throwException?: boolean
): AxiosReturn<TData>;
export function useAxios<TData = any>(
config?: AxiosRequestConfig,
throwException?: boolean
): AxiosReturn<TData>;
export function useAxios<TData = any>(
configUrlThrowException?: AxiosRequestConfig | string | boolean,
configThrowException?: AxiosRequestConfig | boolean,
throwException = false
): AxiosReturn<TData> {
/* istanbul ignore next */
__DEV__ && !axios && console.warn(`[axios] not installed, please install it`);

const config =
!isString(configUrlThrowException) && !isBoolean(configUrlThrowException)
? configUrlThrowException
: isObject(configThrowException)
? (configThrowException as AxiosRequestConfig)
: undefined;
throwException = isBoolean(configUrlThrowException)
? configUrlThrowException
: isBoolean(configThrowException)
? configThrowException
: throwException;

const axiosClient = axios.create(config);
const client = computed(() => axiosClient);
const isCancelled = ref(false);
const cancelledMessage = ref<string | undefined | null>(null);

let cancelToken: CancelTokenSource | undefined = undefined;
const cancel = (message?: string) => {
if (!cancelToken) {
/* istanbul ignore else */
if (__DEV__) {
throw new Error("Cannot cancel because no request has been made");
} else {
return;
}
}
cancelToken.cancel(message);
isCancelled.value = true;
cancelledMessage.value = message;
};

const use = usePromise(
async (request: AxiosRequestConfig | string) => {
cancelToken = axios.CancelToken.source();
isCancelled.value = false;
cancelledMessage.value = null;

const opts = isString(request) ? { url: request } : request;

return axiosClient.request<any, AxiosResponse<TData>>({
cancelToken: cancelToken.token,
...opts
});
},
{
lazy: true,
throwException
}
);

const data = computed<TData>(
() =>
(use.result.value && use.result.value.data) ||
(use.error.value &&
use.error.value.response &&
use.error.value.response.data) ||
null
);
const status = computed<number>(
() =>
(use.result.value && use.result.value.status) ||
(use.error.value &&
use.error.value.response &&
use.error.value.response.status) ||
null
);
const statusText = computed<string>(
() =>
(use.result.value && use.result.value.statusText) ||
(use.error.value &&
use.error.value.response &&
use.error.value.response.statusText) ||
null
);

// if url provided in the config, execute it straight away
// NOTE: `false` is passed to the `exec` to prevent the exception to be thrown
if (typeof configUrlThrowException === "string") {
(use.exec as any)({ ...config, url: configUrlThrowException }, false);
} else if (config && config.url) {
(use.exec as any)(config, false);
}

return {
...use,
client,
data,
status,
statusText,

cancel,
isCancelled,
cancelledMessage
};
}
export { useAxios } from "./axios";
export { makeAxios } from "./makeAxios";

0 comments on commit 4ae8cc5

Please sign in to comment.