Skip to content

Commit

Permalink
chore: initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
fraxken committed Aug 8, 2021
0 parents commit 3322aea
Show file tree
Hide file tree
Showing 27 changed files with 11,026 additions and 0 deletions.
14 changes: 14 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Editor configuration, see https://editorconfig.org
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf

[*.md]
max_line_length = off
trim_trailing_whitespace = false
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/tmp
/dist
6 changes: 6 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "@myunisoft/eslint-config",
"rules": {
"@typescript-eslint/no-non-null-assertion": "off"
}
}
121 changes: 121 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# IDE files
.idea/

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

tmp/
63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Httpie
[httpie](https://github.com/lukeed/httpie) "like" request wrapper that use new Node.js http client [undici](https://github.com/nodejs/undici) under the hood.

## Features

- Implement httpie features.
- Able to automatically detect domains and paths to assign the right Agent (use a LRU cache to avoid repetitive computation).
- [**WIP**] Precise Rate-limit with the package `p-ratelimit`.
- [**WIP**] Built-in retry mechanism with **custom policies**.

Thanks to undici:

- Support [HTTP redirections](https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections) with the `maxRedirections` argument
- Bare metal rate-limit protection by using Undici Agent maximum connections.
- Implement high-level API for undici **stream** and **pipeline** method.
- Optimization and performance of the new client (**around 2.5x faster** than Node.js native http client).
- Work well with **newest** Node.js API [AbortController](https://nodejs.org/dist/latest-v16.x/docs/api/globals.html#globals_class_abortcontroller) to cancel http request.

## Requirements
- [Node.js](https://nodejs.org/en/) version 14 or higher

## Getting Started

This package is available in the Node Package Repository and can be easily installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or [yarn](https://yarnpkg.com).

```bash
$ npm i @myunisoft/httpie
# or
$ yarn add @myunisoft/httpie
```

## Usage example

The MyUnisoft httpie client is very similar to lukeed httpie http client.

```js
import * as httpie from "@myunisoft/httpie";

try {
const response = await httpie.post("https://jsonplaceholder.typicode.com/posts", {
body: {
title: "foo",
body: "bar",
userId: 1
}
});

console.log(response.statusCode);
console.log(response.statusMessage);
console.log(response.data);
}
catch (error) {
console.log(error.message);
console.log(error.statusCode);
console.log(error.headers);
console.log(error.data);
}
```

## API

- [Request API](./docs/request.md)
- [Work and manage Agents](./docs/agents.md)
39 changes: 39 additions & 0 deletions docs/agents.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Agents

Agents are custom constructs that are used to describe services internal to MyUnisoft. However this could also be used for external services.

```js
import { agents } from "@myunisoft/httpie";

console.log(agents); // <- push a new agent in this Array
```

Those agents are described by the following TypeScript interface:
```ts
export interface CustomHttpAgent {
customPath: string;
domains: Set<string>;
agent: Agent;
prod: string;
preprod: string;
dev: string;
}
```

Example with a test custom agent:
```ts
export const test: CustomHttpAgent = {
customPath: "test",
domains: new Set([
"test.domain.fr",
]),
agent: new Agent({
connections: 30
}),
prod: "",
preprod: "",
dev: "https://test.domain.fr"
};
```

The **agent** property is an Undici Agent.
80 changes: 80 additions & 0 deletions docs/request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Request API
The request method is the root method for making http requests. Short method like get or post use it under the hood.

The method **options** and **response** are described by the following TypeScript interfaces:

```ts
export interface RequestOptions {
/** Default: 0 */
maxRedirections?: number;
/** Default: { "user-agent": "myun" } */
headers?: IncomingHttpHeaders;
body?: any;
authorization?: string;
// Could be dynamically computed depending on the provided URI.
agent?: undici.Agent;
}

export interface RequestResponse<T> {
data: T;
headers: IncomingHttpHeaders;
statusMessage: string;
statusCode: number;
}
```

## request< T >(method: string, uri: string | URL, options?: RequestOptions): Promise< RequestResponse< T > >
The first **method** argument take an [HTTP Verb](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) like `GET`, `POST`, `PATCH` etc. The second one **uri** (Uniform Resource Identifier) take a string or a WHATWG URL.

The options allow you to quickly authenticate and add additional headers:
```js
import { request } from "@myunisoft/httpie";

const { data } = await request("GET", "https://test.domain.fr/user/info", {
authorization: "Token here",
headers: {
"society-id": 1
}
});
console.log(data);
```

By default the client will detect the `test.domain.fr` hostname and assign the right Undici Agent. But if necessary they can always be retrieved by hand to be passed to the options.

```js
import { windev } from "@myunisoft/httpie";

console.log(windev);
```

## shorthand methods
Those methods are equivalent to the request arguments (except for `method`)

```ts
export type RequestCallback = <T>(uri: string | URL, options?: RequestOptions) => Promise<RequestResponse<T>>;

export const get = request.bind(null, "GET") as RequestCallback;
export const post = request.bind(null, "POST") as RequestCallback;
export const put = request.bind(null, "PUT") as RequestCallback;
export const del = request.bind(null, "DELETE") as RequestCallback;
export const patch = request.bind(null, "PATCH") as RequestCallback;
```

## error

Errors are triggered if the returned statusCode is equal or higher than 400. It can occur in case of error when reading the response body (for example an invalid JSON).

The triggered error is constructed as follows:

```ts
export function toError<T>(response: RequestResponse<T>) {
const err = new Error(response.statusMessage) as Error & RequestResponse<T>;
err.statusMessage = response.statusMessage;
err.statusCode = response.statusCode;
err.headers = response.headers;
err.data = response.data;

return err;
}
```

16 changes: 16 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
collectCoverage: true,
collectCoverageFrom: [
"**/src/**/**/*.ts"
],
setupFilesAfterEnv: [
"./test/jest.setup.js"
],
testPathIgnorePatterns: [
"/node_modules/",
"/test/fixtures/"
],
setupFiles: ["dotenv/config"]
};
Loading

0 comments on commit 3322aea

Please sign in to comment.