Skip to content

Commit

Permalink
fix(deps): Removes ramda.js (infinitered#281 by @jamonholmgren)
Browse files Browse the repository at this point in the history
  • Loading branch information
jamonholmgren authored Dec 17, 2021
1 parent 3c66fdc commit 638df8b
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 134 deletions.
3 changes: 1 addition & 2 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"presets": [ "es2015" ],
"plugins": [ "ramda" ],
"presets": ["es2015"],
"ignore": "test/*",
"env": {
"development": {
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ Talking to APIs doesn't have to be awkward anymore.
`npm i apisauce --save` or `yarn add apisauce`

- Depends on `axios`.
- Targets ES5.
- Built with ES6.
- Supported in Node and the browser(s) and React Native.
- Compatible with ES5.
- Built with TypeScript.
- Supports Node, the browser, and React Native.

# Quick Start

Expand Down Expand Up @@ -267,7 +267,7 @@ api.addResponseTransform(response => {
Or make it async:

```js
api.addAsyncResponseTransform(async (response) => {
api.addAsyncResponseTransform(async response => {
const something = await AsyncStorage.load('something')
if (something) {
// just mutate the data to what you want.
Expand Down Expand Up @@ -335,7 +335,7 @@ console.log(response.ok) // yay!
# Cancel Request

```js
import {CancelToken} from 'apisauce'
import { CancelToken } from 'apisauce'

const source = CancelToken.source()
const api = create({ baseURL: 'github.com' })
Expand Down
37 changes: 15 additions & 22 deletions examples/github.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,27 @@
import apisauce from '../lib/apisauce'
import R from 'ramda'
import RS from 'ramdasauce'
const apisauce = require('../dist/apisauce.js')

const REPO = 'skellock/apisauce'
const REPO = 'infinitered/apisauce'

const api = apisauce.create({
baseURL: 'https://api.github.com',
headers: {
Accept: 'application/vnd.github.v3+json'
}
Accept: 'application/vnd.github.v3+json',
},
})

// attach a monitor that fires with each request
api.addMonitor(
R.pipe(
RS.dotPath('headers.x-ratelimit-remaining'),
R.concat('Calls remaining this hour: '),
console.log
)
)
api.addMonitor(response => {
const info = `Calls remaining this hour: ${response.headers['x-ratelimit-remaining']}`
console.log(info)
})

// show the latest commit message
api
.get(`/repos/${REPO}/commits`)
.then(RS.dotPath('data.0.commit.message'))
.then(R.concat('Latest Commit: '))
.then(console.log)
api.get(`/repos/${REPO}/commits`).then(response => {
const info = `Latest Commit: ${response.data[0].commit.message}`
console.log(info)
})

// call a non-existant API to show that the flow is identical!
api
.post('/something/bad')
.then(R.props(['ok', 'status', 'problem']))
.then(console.log)
api.post('/something/bad').then(({ ok, status, problem }) => {
console.log({ ok, status, problem })
})
133 changes: 56 additions & 77 deletions lib/apisauce.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,4 @@
import axios, { AxiosResponse, AxiosError } from 'axios'
import {
cond,
isNil,
identity,
is,
T,
curry,
curryN,
gte,
ifElse,
prop,
merge,
dissoc,
keys,
forEach,
pipeP,
partial,
contains,
always,
} from 'ramda'

/**
* Converts the parameter to a number.
Expand All @@ -32,18 +12,23 @@ import {
* @example
* toNumber('7') //=> 7
*/
const toNumber = cond([
[isNil, identity],
[is(Number), identity],
[T, x => Number(x)],
])
const toNumber = (value: any): number => {
// if value is a Date, convert to a number
if (value instanceof Date) {
return value.getTime()
}

if (typeof value === 'number' || value === null || value === undefined) {
return value
}

return Number(value)
}

/**
* Given a min and max, determines if the value is included
* in the range.
*
* This function is curried.
*
* @sig Number a -> a -> a -> b
* @param {Number} the minimum number
* @param {Number} the maximum number
Expand All @@ -55,13 +40,7 @@ const toNumber = cond([
* isWithin(1, 5, 5) //=> true
* isWithin(1, 5, 5.1) //=> false
*/
const isWithin = curryN(3, (min, max, value) => {
const isNumber = is(Number)
return isNumber(min) && isNumber(max) && isNumber(value) && gte(value, min) && gte(max, value)
})

// a workaround to deal with __ not being available from the ramda types in typescript
const containsText = curryN(2, (textToSearch, list) => contains(list, textToSearch))
const isWithin = (min: number, max: number, value: number): boolean => value >= min && value <= max

/**
* Are we dealing with a promise?
Expand Down Expand Up @@ -91,10 +70,9 @@ export const CANCEL_ERROR = 'CANCEL_ERROR'

const TIMEOUT_ERROR_CODES = ['ECONNABORTED']
const NODEJS_CONNECTION_ERROR_CODES = ['ENOTFOUND', 'ECONNREFUSED', 'ECONNRESET']
const in200s = isWithin(200, 299)
const in400s = isWithin(400, 499)
const in500s = isWithin(500, 599)
const statusNil = ifElse(isNil, always(undefined), prop('status'))
const in200s = (n: number): boolean => isWithin(200, 299, n)
const in400s = (n: number): boolean => isWithin(400, 499, n)
const in500s = (n: number): boolean => isWithin(500, 599, n)

/**
* What's the problem for this axios response?
Expand All @@ -105,41 +83,39 @@ export const getProblemFromError = error => {
if (axios.isCancel(error)) return CANCEL_ERROR

// then check the specific error code
return cond([
// if we don't have an error code, we have a response status
[isNil, () => getProblemFromStatus(statusNil(error.response))],
[containsText(TIMEOUT_ERROR_CODES), always(TIMEOUT_ERROR)],
[containsText(NODEJS_CONNECTION_ERROR_CODES), always(CONNECTION_ERROR)],
[T, always(UNKNOWN_ERROR)],
])(error.code)
if (!error.code) return getProblemFromStatus(error.response.status)
if (TIMEOUT_ERROR_CODES.includes(error.code)) return TIMEOUT_ERROR
if (NODEJS_CONNECTION_ERROR_CODES.includes(error.code)) return CONNECTION_ERROR
return UNKNOWN_ERROR
}

type StatusCodes = undefined | number

/**
* Given a HTTP status code, return back the appropriate problem enum.
*/
export const getProblemFromStatus = status => {
return cond([
[isNil, always(UNKNOWN_ERROR)],
[in200s, always(NONE)],
[in400s, always(CLIENT_ERROR)],
[in500s, always(SERVER_ERROR)],
[T, always(UNKNOWN_ERROR)],
])(status)
export const getProblemFromStatus = (status: StatusCodes) => {
if (!status) return UNKNOWN_ERROR
if (in200s(status)) return NONE
if (in400s(status)) return CLIENT_ERROR
if (in500s(status)) return SERVER_ERROR
return UNKNOWN_ERROR
}

/**
* Creates a instance of our API using the configuration.
*/
export const create = config => {
// combine the user's headers with ours
const headers = merge(DEFAULT_HEADERS, config.headers || {})
const headers = { ...DEFAULT_HEADERS, ...(config.headers || {}) }

let instance
if (config.axiosInstance) {
// use passed axios instance
instance = config.axiosInstance
} else {
const combinedConfig = merge(DEFAULT_CONFIG, dissoc('headers', config))
const configWithoutHeaders = { ...config, headers: undefined }
const combinedConfig = { ...DEFAULT_CONFIG, ...configWithoutHeaders }
// create the axios instance
instance = axios.create(combinedConfig)
}
Expand Down Expand Up @@ -167,7 +143,7 @@ export const create = config => {

// sets headers in bulk
const setHeaders = headers => {
forEach(header => setHeader(header, headers[header]), keys(headers))
Object.keys(headers).forEach(header => setHeader(header, headers[header]))
return instance
}

Expand All @@ -192,18 +168,21 @@ export const create = config => {
return instance.defaults.baseURL
}

type RequestsWithoutBody = 'get' | 'head' | 'delete' | 'link' | 'unlink'
type RequestsWithBody = 'post' | 'put' | 'patch'

/**
* Make the request for GET, HEAD, DELETE
*/
const doRequestWithoutBody = (method, url, params = {}, axiosConfig = {}) => {
return doRequest(merge({ url, params, method }, axiosConfig))
const doRequestWithoutBody = (method: RequestsWithoutBody) => (url: string, params = {}, axiosConfig = {}) => {
return doRequest({ ...axiosConfig, url, params, method })
}

/**
* Make the request for POST, PUT, PATCH
*/
const doRequestWithBody = (method, url, data, axiosConfig = {}) => {
return doRequest(merge({ url, method, data }, axiosConfig))
const doRequestWithBody = (method: RequestsWithBody) => (url: string, data, axiosConfig = {}) => {
return doRequest({ ...axiosConfig, url, method, data })
}

/**
Expand All @@ -219,7 +198,7 @@ export const create = config => {
if (requestTransforms.length > 0) {
// overwrite our axios request with whatever our object looks like now
// axiosRequestConfig = doRequestTransforms(requestTransforms, axiosRequestConfig)
forEach(transform => transform(axiosRequestConfig), requestTransforms)
requestTransforms.forEach(transform => transform(axiosRequestConfig))
}

// add the async request transforms
Expand All @@ -235,11 +214,11 @@ export const create = config => {
}

// after the call, convert the axios response, then execute our monitors
const chain = pipeP(
convertResponse(toNumber(new Date())),
// partial(convertResponse, [toNumber(new Date())]),
runMonitors,
)
const startTime = toNumber(new Date())
const chain = async response => {
const ourResponse = await convertResponse(startTime, response)
return runMonitors(ourResponse)
}

return instance
.request(axiosRequestConfig)
Expand All @@ -265,7 +244,7 @@ export const create = config => {
/**
* Converts an axios response/error into our response.
*/
const convertResponse = curry(async (startedAt: number, axiosResult: AxiosResponse | AxiosError) => {
const convertResponse = async (startedAt: number, axiosResult: AxiosResponse | AxiosError) => {
const end: number = toNumber(new Date())
const duration: number = end - startedAt

Expand Down Expand Up @@ -294,7 +273,7 @@ export const create = config => {
data,
}
if (responseTransforms.length > 0) {
forEach(transform => transform(transformedResponse), responseTransforms)
responseTransforms.forEach(transform => transform(transformedResponse))
}

// add the async response transforms
Expand All @@ -310,7 +289,7 @@ export const create = config => {
}

return transformedResponse
})
}

// create the base object
const sauce = {
Expand All @@ -332,14 +311,14 @@ export const create = config => {
setBaseURL,
getBaseURL,
any: doRequest,
get: partial(doRequestWithoutBody, ['get']),
delete: partial(doRequestWithoutBody, ['delete']),
head: partial(doRequestWithoutBody, ['head']),
post: partial(doRequestWithBody, ['post']),
put: partial(doRequestWithBody, ['put']),
patch: partial(doRequestWithBody, ['patch']),
link: partial(doRequestWithoutBody, ['link']),
unlink: partial(doRequestWithoutBody, ['unlink']),
get: doRequestWithoutBody('get'),
delete: doRequestWithoutBody('delete'),
head: doRequestWithoutBody('head'),
post: doRequestWithBody('post'),
put: doRequestWithBody('put'),
patch: doRequestWithBody('patch'),
link: doRequestWithoutBody('link'),
unlink: doRequestWithoutBody('unlink'),
}
// send back the sauce
return sauce
Expand Down
16 changes: 10 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
{
"version": "2.1.3",
"author": "Steve Kellock <[email protected]>",
"author": {
"name": "Infinite Red",
"email": "[email protected]",
"url": "https://github.com/infinitered/ignite"
},
"ava": {
"require": [
"babel-core/register"
]
},
"dependencies": {
"axios": "^0.21.4",
"ramda": "^0.25.0"
"axios": "^0.21.4"
},
"description": "Axios + standardized errors + request/response transforms.",
"devDependencies": {
Expand All @@ -19,14 +22,14 @@
"babel-cli": "^6.26.0",
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.3",
"babel-plugin-ramda": "^1.6.1",
"babel-preset-es2015": "^6.24.1",
"husky": "^1.3.1",
"lint-staged": "^8.1.0",
"np": "3.0.4",
"npm-run-all": "^4.1.5",
"nyc": "^11.8.0",
"prettier": "^1.15.3",
"ramda": "^0.25.0",
"ramdasauce": "^2.1.0",
"rollup": "^0.59.1",
"rollup-plugin-babel": "^3.0.4",
Expand Down Expand Up @@ -56,7 +59,7 @@
"name": "apisauce",
"repository": {
"type": "git",
"url": "https://github.com/skellock/apisauce.git"
"url": "https://github.com/infinitered/apisauce.git"
},
"scripts": {
"build": "BABEL_ENV=production rollup -c",
Expand All @@ -70,7 +73,8 @@
"test:unit": "ava -s",
"ci:publish": "yarn semantic-release",
"semantic-release": "semantic-release",
"format": "prettier --write \"{**/*.ts,.circleci/**/*.js}\" --loglevel error && tslint -p . --fix"
"format": "prettier --write \"{**/*.ts,.circleci/**/*.js}\" --loglevel error && tslint -p . --fix",
"example": "node ./examples/github.js"
},
"prettier": {
"semi": false,
Expand Down
Loading

0 comments on commit 638df8b

Please sign in to comment.