Skip to content

Commit

Permalink
Initial code base migrated from NGN 1.x.x. Supports ES Module syntax …
Browse files Browse the repository at this point in the history
…with both callback and promise-based HTTP requests to close #1. Now includes a URL class to fix #4. A Credential class abstracts username/password/token auth. The Credential class supports Authorization and Proxy-Authorization to close #2. Some constants are included in the library, as requested in #6, but only those relevant to network _requests_.
  • Loading branch information
coreybutler committed Aug 30, 2020
0 parents commit 896100d
Show file tree
Hide file tree
Showing 27 changed files with 4,995 additions and 0 deletions.
92 changes: 92 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: Test Suite

on:
push:
branches:
- master
pull_request:
branches:
- master

jobs:
build:
name: 'Run Tests'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
name: Checkout Code

- name: List Directory Contents (for Troubleshooting)
run: |
pwd
ls -l
- uses: actions/setup-node@v1
name: Setup Node.js
with:
node-version: '14'

- uses: actions/cache@v2
name: Establish npm Cache
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- uses: actions/cache@v2
name: Establish Docker Cache
id: cache
with:
path: docker-cache
key: ${{ runner.os }}-docker-${{ github.sha }}
restore-keys: |
${{ runner.os }}-docker-
- name: Load cached Docker layers
run: |
if [ -d "docker-cache" ]; then
cat docker-cache/x* > my-image.tar
docker load < my-image.tar
rm -rf docker-cache
fi
- name: Download Dev Tooling
id: setup
run: |
echo ${{ secrets.GH_DOCKER_TOKEN }} | docker login https://docker.pkg.github.com -u ${{ secrets.GH_DOCKER_USER }} --password-stdin
base=$(curl -L -s 'https://registry.hub.docker.com/v2/repositories/author/dev-base/tags?page_size=1'|jq '."results"[]["name"]')
base=$(sed -e 's/^"//' -e 's/"$//' <<<"$base")
echo Retrieving author/dev/dev-base:$base
docker pull author/dev-base:$base
# docker pull docker.pkg.github.com/author/dev/dev-base:$base
deno=$(curl -L -s 'https://registry.hub.docker.com/v2/repositories/author/dev-deno/tags?page_size=1'|jq '."results"[]["name"]')
deno=$(sed -e 's/^"//' -e 's/"$//' <<<"$deno")
echo Retrieving author/dev/dev-deno:$deno
# docker pull docker.pkg.github.com/author/dev/dev-deno:$deno
docker pull author/dev-deno:$deno
browser=$(curl -L -s 'https://registry.hub.docker.com/v2/repositories/author/dev-browser/tags?page_size=1'|jq '."results"[]["name"]')
browser=$(sed -e 's/^"//' -e 's/"$//' <<<"$browser")
echo Retrieving author/dev/dev-browser:$browser
# docker pull docker.pkg.github.com/author/dev/dev-browser:$browser
docker pull author/dev-browser:$browser
node=$(curl -L -s 'https://registry.hub.docker.com/v2/repositories/author/dev-node/tags?page_size=1'|jq '."results"[]["name"]')
node=$(sed -e 's/^"//' -e 's/"$//' <<<"$node")
echo Retrieving author/dev/dev-node:$node
# docker pull docker.pkg.github.com/author/dev/dev-node:$node
docker pull author/dev-node:$node
version=$(npm show @author.io/dev version)
echo $version
npm i -g @author.io/dev@$version
dev -v
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Test
if: success()
run: |
npm test
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
*.log
*.old
.*
!.github
!.*ignore
!.*.yml
!.*rc
!.npmrc
env.json
/**/env.json
node_modules
6 changes: 6 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
build
test
examples
.*
*.log
*.md
22 changes: 22 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
MIT License

Copyright (c) 2020 Corey Butler


Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
187 changes: 187 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# Network NGN Plugin

The NGN network plugin provides two major building blocks:

1. HTTP `Client`
1. HTTP `Resource`

The following features are also included:

1. `URL`
1. `Fetch`
1. `INTERFACES`
1. `HOSTNAME`

## HTTP Client

The HTTP client provides an intuitive way to execute HTTP requests. All major HTTP methods are supported (`GET`, `POST`, `PUT`, `DELETE`, `HEAD`, `OPTIONS`, & `TRACE`). Two additional "helper" methods exist for accessing JSON data: `JSON` and `JSONP` (only used in browser runtimes).

```javascript
import { Client } from '@ngnjs/net'

const client = new Client()

client.GET('https://domain.com').then(response => console.log(response.body)).catch(console.error)

client.GET('https://domain.com', function (response) {
console.log(response)
})

client.JSON('https://domain.com/data.json', data => {
console.log(data)
})
```

This client is intentionally simple. It behaves more like an API tool, similar to Postman. Issuing a request will return a response. This differs from some request libraries, which throw errors for certain HTTP status codes, such as `404`. These are _valid_ responses. The client treats them as such. The client will only throw an error when a code problem occurs. Developers are free to handle network responses however they see fit.

The client natively supports headers, query strings/parameters, promises, and callbacks. This differs from other libraries, which rely on dependencies. The native capability allows this client to run consistently across runtimes, such as browsers, Node.js, and Deno.

Some runtimes do not support all of the features developers are used to in the browser. For example, the browser Fetch API supports [caching](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy), CORS mode, [referral policies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy), [subresource integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity), and [abort controllers](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). Node.js is does not natively support most of these, while Deno is missing a few. These features have been stubbed in the client. Using them will not break an application, but they also won't do anything by default. If these features are important in your application, use the `@ngnjs/libnet-node` library, which polyfills these features for Node.js.

## HTTP Resource

An HTTP resource is a special HTTP client. It applies common values to all requests. For example:

```javascript
const API = new Resource({
baseUrl: 'https://api.domain.com',
headers: {
'x-version': 'v1'
},
token: 'mytoken'
})

API.JSON('/endpoint').then(data => { ... }).catch(console.error)
```

In the example above, a request is made to `https://api.domain.com/endpoint`. Two headers are applied automatically:

1. `x-version: v1`
1. `Authorization: Bearer mytoken`.

Any option that can be applied to a Request can be applied to a resource (including query parameters and URL hashes).

### Primary Purpose

Resources make it easy to organize communications with remote services. They can be used to create custom API clients, including multiple clients per application.

### Extended Configuration Options

Resources also have a few special configuration attributes:

- `nocache` - set this to `true` to avoid caching. This overrides the `Cache-Control` header.
- `unique` - this this to `true` to automatically append a unique query parameter to all requests (cache busting).
- `useragent` - Specify a custom user agent name. This can be helpful when interacting with API's which require an identifying user agent.
- `uniqueagent` - set this to `true` to generate a unique ID to append to the `useragent` name. This helps guarantee every request comes from a unique user agent name.
- `tokenRenewalNotice` - optionally set this to trigger a notification event before an auth token expires (see Auth Tokens).

### Auth Tokens

When interacting with API's, it is common for auth tokens to expire after a period of time. The `setToken` method will accept the expiration date/time, automatically removing the token when it expires.

By default, a token expiration warning event (`token.pending.expiration`) is triggered 10 seconds before a token expires. The lead time can be modified using the `tokenRenewalNotice` configuration option.

Token renewal notices were not designed to auto-renew auth tokens. They were designed to notify the application before a token is expired/removed. Applications can use this feature to renew tokens before they expire (or choose not to).

## URL

The `URL` class, often aliases as `Address` is an enhanced version of the [URL API](https://developer.mozilla.org/en-US/docs/Web/API/URL).

### Query Parameters

The `searchParams` feature of the URL API is still available, but sometimes it is just simpler to reference a query object (instead of a map-like object). For this purpose, a `query` attribute is available, providing a common key/value structure of all the query parameters (readable and writable).

The `querystring` attribute also provides a convenient way to read/write the full query string.

### Ports & Protocols

In the process of manipulating URL's, it is common to need to change the port. This URL class automatically recognizes default ports for several known protocols (`http`, `https`, `ssh`, `ldap`, `sldap`, `ftp`, `ftps`, `sftp`). Custom default ports can also be specified using the `setDefaultProtocolPort()` method. When the protocol is changed, it does not update the port automatically, but the `update.protocol` event is triggered, allowing applications to determine if the port should be updated.

Setting `port` to `'default'` will always use the default port of the specified protocol. The `resetPort()` method is a shortcut for doing this.

### Cross Origin Features

The `local` attribute determines whether a URL is local to the system or not. In browsers, this detects whether the URL's origin is the same as the page. In backend runtimes, this is compared to the system `hostname` (server name), as well as the local IP addresses and any other local interfaces.

The `isSameOrigin()` method provides a quick and convenient way to determine if a different URL shares the same origin (i.e. not a cross-origin URL). This method optionally supports strict protocol matching.

These features can help assess and minimize CORS errors.

### URL Formatting & Output

The URL class has two methods, `toString()` and `formatString`.

`toString()` differs from the URL API. It accepts a configuration argument, allowing certain aspects of the URI to be ignored or forced. For example:

```javascript
const myUrl = 'http://locahost'

console.log(myUrl.toString({ port: true }))
// Outputs http://localhost:80
```

The `formatString` accepts a template, which will be populated with the attributes of the URL. For example:

```javascript
const myUrl = 'http://jdoe:secret@localhost/path.html'

console.log(myUrl.formatString('{{username}} accessing {{path}}'))
// Outputs jdoe acessing path.html
```

## Additional Features

The Client, Resource, and URL classes all implement an `NGN.EventEmitter`. This means they'll fire change events.

## Known Issues

### Fetch API: ReferrerPolicy & Cache

Deno doesn't support fetch ReferrerPolicy & Cache. Deno is working on cache support, but will likely not implement ReferrerPolicy. Node.js doesn't support these either, but the `@ngnjs/libnet-node` plugin can polyfill such features.

ReferrerPolicy is less likely to be necessary in non-browser environments, with the exception of a limited set of proxy and API applications.

The ReferrerPolicy polyfill in `@ngnjs/libnet-node` currently uses the `os` module to identify the server hostname & IP address. This is used to help determine when a referrer is on the same host or not. The hostname feature does not yet exist in Deno, but is on the Deno roadmap. When support for this is available, a ReferrerPolicy polyfill will be made for Deno (if there is enough interest).

--

Everything below is boilerplate from the plugin generation tool (can be removed when ready)
--
A plugin module for NGN.

## Usage

### Via CDN (Browser/Deno)

```javascript
import NGN from 'https://cdn.skypack.dev/ngn'
import Network from 'https://cdn.skypack.dev/@ngnjs/network'

const feature = new Network()

console.log(feature)
```

### Node

`npm i ngn @ngnjs/network -S`

```javascript
import NGN from 'ngn'
import Network from '@ngnjs/network'

const feature = new Network()

console.log(feature)
```

## Plugin Authoring Instructions (Remove these)

This project template supports creation of NGN plugins. This template can be used directly, but was designed to be implemented with [NGND (NGN Dev) CLI tool](https"//github/com/ngnjs/cli).

The template contains:

1. Starter Source Code
1. Unit Test Boilerplate
1. CI/CD Runner
1. Autopublishing (triggered on new `package.json` versions in master branch)
Loading

0 comments on commit 896100d

Please sign in to comment.