# @crawly/grasshopper-lib-proxy

Está biblioteca tem como objetivo gerenciar as configurações de proxy utilizadas nos projetos.

- [Utilização](#utilização)
  * [Instalação](#instalação)
  * [Valores padrão](#valores-padr-o)
  * [Como funciona](#como-funciona)
  * [Utilizando com uma variável de ambiente customizada](#utilizando-com-uma-variável-de-ambiente-customizada)
  * [Utilizando com uma lista de proxies pré-definida](#utilizando-com-uma-lista-de-proxies-pré-definida  )
  * [Utilizando proxy com configuração dinâmica](#utilizando-proxy-com-configuração-dinâmica)
  * [Obter metadados da instância atual de proxy](#obter-metadados-da-instância-atual-de-proxy)
  * [Sobrescrever o valor da instância atual de proxy](#sobrescrever-o-valor-da-instância-atual-de-proxy)
- [Métodos](#métodos)
  - ProxyBuilder
    * [ProxyBuilder.withKey(key: string): Proxy](#withkeykey-string-proxy)
    * [ProxyBuilder.withDynamicKey(key: string): Proxy](#withdynamickeykey-string-proxy)
    * [ProxyBuilder.withTunnelKey(key: string): Proxy](#withtunnelkeykey-string-proxy)
    * [ProxyBuilder.withRandomToken(): Proxy](#withrandomtoken-proxy)
    * [ProxyBuilder.withProxyList(list?: string): Proxy](#withproxylistlist-string-proxy)
    * [ProxyBuilder.withProxyDynamic(url?: string): Proxy](#withproxydynamicurl-string-proxy)
    * [ProxyBuilder.withRandomInstance(): Proxy](#withrandominstance-proxy)
    * [ProxyBuilder.getRandomToken(): string](#getrandomtoken-string)
    * [ProxyBuilder.getDynamicInstance(): Promise<string>](#getdynamicinstance-promise)
    * [ProxyBuilder.getInstance(): Promise<string>](#getinstance-promise)
    * [ProxyBuilder.getTunnel(): boolean | undefined](#gettunnel-boolean-undefined)
    * [ProxyBuilder.getProxyList(): string[]](#getproxylist-string)
    * [ProxyBuilder.getMetadata(url?: string): Promise<Metadata>](#getmetadataurl-string-promise)
  - ProxyOverride
    * [proxyOverride(event: any): void](#proxyoverrideevent-any-void)

## Utilização

### Instalação

```bash
npm install @crawly/proxy
```

### Valores padrão

Os valores abaixo são *hardcoded*, mas podem ser sobrescritos conforme a necessidade.

* `PROXY_LIST`: nome da variável de ambiente para a lista de *proxies*;
* `PROXY_TUNNEL`: nome da variável de ambiente para a configuração *[`tunnel`](https://en.wikipedia.org/wiki/HTTP_tunnel)*;
* `PROXY_DYNAMIC`: nome da variável de ambiente que deverá conter a URL para requisição em *proxies* obtidas de forma dinamica;

### Como funciona

A biblioteca segue o padrão *[Builder](https://en.wikipedia.org/wiki/Builder_pattern)*, adicionando as configurações ao contexto, e ao chamar o método `.getInstance()` é retornada a proxy atual.

```typescript
import { ProxyBuilder } from '@crawly/proxy';

const proxy = await (new ProxyBuilder())
  .withProxyList()
  .withRandomToken()
  .withRandomInstance()
  .getInstance();
```

### Utilizando com uma variável de ambiente customizada
Para utilizar uma variável de ambiente diferente de `PROXY_LIST`, basta fazer uma chamada ao método `.withKey(key: string)` informando o nome da nova variável de ambiente.

```typescript
const proxy = await (new ProxyBuilder())
  .withKey('PROXY_LIST_GOL')
  .withProxyList()
  .withRandomToken()
  .withRandomInstance()
  .getInstance();
```

### Utilizando com uma lista de proxies pré-definida
Para utilizar uma lista de proxies, sobrescrevendo a configuração da variável de ambiente `PROXY_LIST`, basta informar as proxies desejadas na chamada do método `.withProxyList(proxies)`.
O parâmetro deve ser uma string separada por vírgulas, semelhante ao formato utilizado para a variável de ambiente.

```typescript
const proxyList = 'http://proxy1.com:2222'
  + ',http://proxy2.com:3333'
  + ',http://proxy3.com:5555'
  + ',http://proxy4.com:2289';

const proxy = await (new ProxyBuilder())
  .withProxyList(proxyList)
  .withRandomToken()
  .withRandomInstance()
  .getInstance();
```

### Utilizando proxy com configuração dinâmica
A configuração dinâmica consiste em fazer uma requisição a um *endpoint* pré-definido, que deverá retornar um objeto contendo as informações: username, password, hostname, port.

Para utilizar a configuração dinâmica, o valor da variável de ambiente `PROXY_LIST` precisa estar definido como `DYNAMIC`. O *endpoint* a ser requisitado precisa estar definido na variável de ambiente `PROXY_DYNAMIC`, conforme exemplo abaixo.

```bash
PROXY_LIST=DYNAMIC
PROXY_DYNAMIC=http://exemplo.com
```

```typescript
const proxy = await (new ProxyBuilder())
  .withProxyList()
  .withRandomToken()
  .withRandomInstance()
  .getInstance();
```

**NOTA:** Assim como podemos utilizar uma variável de ambiente diferente para a lista de proxies, podemos utilizar o método `.withDynamicKey(key)` para redefinir a variável de ambiente que contém o *endpoint* para a proxy dinâmica.



### Obter metadados da instância atual de proxy
Ao utilizar o método `.getMetadata()`, será retornado um objeto contendo as informações da instancia atual de proxy, conforme exemplo.


```typescript
const proxyList = `http://sb7257jvigmyz0:sb7257jvigmyz1@hostname.com:2222`;

const proxyData = await (new ProxyBuilder())
  .withProxyList(proxyList)
  .withRandomToken()
  .withRandomInstance()
  .getMetadata();
```

Retorno:
```javascript
{
  username: 'sb7257jvigmyz0',
  password: 'sb7257jvigmyz1',
  hostname: 'hostname.com',
  protocol: 'http',
  port: '2222'
}
```

### Sobrescrever o valor da instância atual de proxy

Ver utilização da função [proxyOverride](#proxyoverride-event--any---void).

## Métodos

### .withKey(key: string): Proxy
Utilizado para redefinir o nome da variável de ambiente a ser utilizada para a lista de proxies.

**NOTA:** ver [utilizando com uma variável de ambiente customizada](#utilizando-com-uma-variavel-de-ambiente-customizada).

### .withDynamicKey(key: string): Proxy
Utilizado para redefinir o nome da variável de ambiente a ser utilizada para a lista de proxies.

```typescript
const proxy = await (new ProxyBuilder())
  .withKey('PROXY_LIST_GOL')
  .withDynamicKey('PROXY_DYNAMIC_GOL')
  .withProxyList()
  .withRandomToken()
  .withRandomInstance()
  .getInstance();
```

### .withTunnelKey(key: string): Proxy
Utilizado para redefinir o nome da variável de ambiente a ser utilizada para definir se a configuração `tunnel` estará habilitada nas requisições.

```typescript
const proxyTunnel = await (new ProxyBuilder())
  .withTunnelKey('PROXY_TUNNEL_GOL')
  .getTunnel();
```

### .withRandomToken(): Proxy
Define o token randômico para ser concatenado ao nome de usuário da proxy atual, conforme a [documentação das proxies](https://crawly.atlassian.net/wiki/spaces/CE/pages/42106939/Proxy+providers): É possível adicionar flags ao Username, como `-session-<string>` para manter sticky ips.

### .withProxyList(list?: string): Proxy
Define a lista de proxies que será utilizada. Por padrão, o valor é obtido a partir da variável de ambiente `PROXY_LIST`, ou utilizando o parametro recebido (string de elementos separados por vírgulas).

A lista de proxies informada por parâmetro, tem maior prioridade sobre a variável de ambiente `PROXY_LIST`.

### .withProxyDynamic(url?: string): Proxy
Define a URL que será utilizada para obter a proxy dinâmica. Por padrão, o valor é obtido a partir da variável de ambiente `PROXY_DYNAMIC`, ou utilizando o parametro recebido (string).

A URL informada por parâmetro tem maior prioridade sobre a variável de embiente `PROXY_DYNAMIC`.

### .withRandomInstance(): Proxy
Define, de forma randômica, qual item da lista de proxies será utilizada para a instância atual.

### .getRandomToken(): string
Retorna o valor do token utilizado pela intancia de proxy atual.

```typescript
const builder = new ProxyBuilder();
const proxy = await builder
  .withProxyList()
  .withRandomToken()
  .withRandomInstance()
  .getInstance();
const token = builder.getRandomToken();
```

### .getDynamicInstance(): Promise<string>
Retorna uma instância de proxy conforme a requisição feita para um endpoint externo.

Este método é chamado internamente de `.getInstance()` quando o valor da variável de ambiente `PROXY_LIST` for igual a `DYNAMIC`, mas pode ser chamado diretamente.

```typescript
const proxy = (new ProxyBuilder())
  .getDynamicInstance();
```

### .getInstance(): Promise<string>
Método final do Builder, retorna uma promise com a string final da proxy atual.

```typescript
const proxy = await (new ProxyBuilder())
  .withProxyList()
  .withRandomInstance()
  .getInstance();
```

### .getTunnel(): boolean | undefined
Retorna o valor atual da variável de ambiente `PROXY_TUNNEL`, ou variável redefinida pelo método `.withTunnelKey(key`;

```typescript
const proxyTunnel = await (new ProxyBuilder())
  .getTunnel();
```

### .getProxyList(): string[]
Retorna a lista de proxies conforme o valor definido na variável de ambiente, ou valor redefinido pelo metódo `.withProxyList(list)`.

```typescript
const proxyList = await (new ProxyBuilder())
  .withProxyList()
  .getProxyList();
```

### .getMetadata(url?: string): Promise<Metadata>
Método final do Builder, retorna uma promise que contém os dados da instância de proxy atual.

Ver [obter metadados da instância atual de proxy](obter-metadados-da-instancia-atual-de-proxy).

### proxyOverride(event: any): void
Função utilizada para sobrescrever o valor das variáveis de ambiente relacionadas à proxy atual.

Recebe como parametro um objeto, que pode ser um `event` quando usado com `lambda`, ou uma `request` quando usado com `express`.

A função busca pela variável de ambiente contendo o prefixo `PROXY_LIST`, se o header contiver uma propriedade correspondente `X-PROXY_LIST`, o valor da variável de ambiente será redefinido.

> Ex.: se existe uma variável de ambiente `PROXY_LIST_GOL`, a função irá procurar no header a propriedade `X-PROXY_LIST_GOL`.

```typescript
// lambda
export const lambda: Handler = async (event: any, context: Context, callback: Callback) => {
  proxyOverride(event);
  ...
}
```
```typescript
// express
const searchHandler = async (req: Request, res: Response): Promise<void> => {
  proxyOverride(req);
  ...
}
```

