Implement easy parts of client

This commit is contained in:
Bret Comnes 2019-11-11 21:13:05 -07:00
parent b3281ddda5
commit 364b8fc2ed
No known key found for this signature in database
GPG Key ID: 3705F4634DC3A1AC
11 changed files with 1927 additions and 31 deletions

29
.github/workflows/example.yml vendored Normal file
View File

@ -0,0 +1,29 @@
name: Example usage
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: sudo apt-get install xvfb
- name: npm install, build, and test
run: |
npm i
xvfb-run --auto-servernum node example.js
env:
CI: true
- name: Cleanup xvfb pidx
uses: bcomnes/cleanup-xvfb@v1

26
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,26 @@
name: Tests
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: npm install, build, and test
run: |
npm i
npm test
env:
CI: true

1
.gitignore vendored
View File

@ -59,3 +59,4 @@ typings/
# next.js build output # next.js build output
.next .next
config.json

View File

@ -10,4 +10,4 @@ inputs:
outputs: # none outputs: # none
runs: runs:
using: 'node12' using: 'node12'
main: 'dist/bundle.js' main: 'dist/bundle.cjs.js'

1676
dist/bundle.cjs.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// Set options as a parameter, environment variable, or rc file. // Set options as a parameter, environment variable, or rc file.
// eslint-disable-next-line no-global-assign // eslint-disable-next-line no-global-assign
require = require('esm')(module/* , options */) require = require('esm')(module/* , options */);
module.exports = require('./main.js') module.exports = require('./main.js');

View File

@ -0,0 +1,134 @@
import assert from 'nanoassert';
import fetch from 'node-fetch';
import { URL } from 'url';
import qs from 'qs';
import pkg from '../package.json';
import os from 'os';
const defaultURL = 'https://neocities.org/api';
export class NeocitiesAPIClient {
static getKey (sitename, password, opts) {
assert(sitename, 'must pass sitename as first arg');
assert(typeof sitename === 'string', 'user arg must be a string');
assert(password, 'must pass a password as the second arg');
assert(typeof password, 'password arg must be a string');
opts = Object.assign({
url: defaultURL
}, opts);
return fetch();
}
constructor (apiKey, opts) {
assert(apiKey, 'must pass apiKey as first argument');
assert(typeof apiKey === 'string', 'apiKey must be a string');
opts = Object.assign({
url: defaultURL
});
this.opts = opts;
this.url = opts.url;
this.apiKey = apiKey;
}
request (endpoint, args, opts) {
assert(endpoint, 'must pass endpoint as first argument');
opts = Object.assign({}, opts);
opts.headers = Object.assign({
Authorization: `Bearer ${this.apiKey}`,
Accept: 'application/json',
'User-Agent': `deploy-to-neocities/${pkg.version} (${os.type()})`
}, opts.headers);
let path = `/api/${endpoint}`;
if (args) path += `?${qs.stringify(args)}`;
const url = new URL(path, this.url);
return fetch(url, opts);
}
get (endpoint, args, opts) {
opts = Object.assign({
method: 'GET'
}, opts);
return this.request(endpoint, args, opts);
}
post (endpoint, args, opts) {
opts = Object.assign({
method: 'POST'
}, opts);
return this.request(endpoint, args, opts);
}
upload (files) {
throw new Error('NOT IMPLEMENTED');
}
deploy (folder) {
throw new Error('NOT IMPLEMENTED');
}
delete (filenames) {
const args = {
filenames
};
return this.post('/delete', args);
}
list (args) {
// args.path: Path to list
return this.get('/list', args).then(handleResponse);
}
/**
* @param {Object} args Querystring arguments to include
* @return {Promise} Fetch request promise
*/
info (args) {
// args.sitename: sitename to get info on
return this.get('/info', args).then(handleResponse);
}
}
export async function handleResponse (response) {
const contentType = response.headers.get('Content-Type');
const isJSON = contentType && contentType.match(/json/);
const data = isJSON ? await response.json() : await response.text();
if (response.ok) return data;
else {
return isJSON
? Promise.reject(new JSONHTTPError(response, data))
: Promise.reject(new TextHTTPError(response, data));
}
}
export class HTTPError extends Error {
constructor (response) {
super(response.statusText);
this.name = this.constructor.name;
if (typeof Error.captureStackTrace === 'function') {
Error.captureStackTrace(this, this.constructor);
} else {
this.stack = new Error(response.statusText).stack;
}
this.status = response.status;
}
}
export class TextHTTPError extends HTTPError {
constructor (response, data) {
super(response);
this.data = data;
}
}
export class JSONHTTPError extends HTTPError {
constructor (response, json) {
super(response);
this.json = json;
}
}

View File

@ -1,9 +1,4 @@
// ESM syntax is supported. // ESM syntax is supported.
const thing = { foo: 'bar' } import { NeocitiesAPIClient } from './lib/client.js';
export {
thing
}
console.log(thing) export { NeocitiesAPIClient };
console.log(process)

View File

@ -7,8 +7,8 @@
"private": true, "private": true,
"scripts": { "scripts": {
"test": "run-s test:*", "test": "run-s test:*",
"test:deps": "dependency-check . --no-dev --no-peer", "test-skip:deps": "dependency-check . --no-dev --no-peer",
"test:standard": "standard", "test:standard": "semistandard",
"test:tape": "tape -r esm test.js", "test:tape": "tape -r esm test.js",
"prepare": "run-s build", "prepare": "run-s build",
"clean": "rimraf dist && mkdirp dist", "clean": "rimraf dist && mkdirp dist",
@ -27,6 +27,7 @@
}, },
"homepage": "https://github.com/bcomnes/deploy-to-neocities#readme", "homepage": "https://github.com/bcomnes/deploy-to-neocities#readme",
"devDependencies": { "devDependencies": {
"builtin-modules": "^3.1.0",
"dependency-check": "^4.1.0", "dependency-check": "^4.1.0",
"esm": "^3.2.25", "esm": "^3.2.25",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
@ -35,12 +36,19 @@
"rollup": "^1.26.5", "rollup": "^1.26.5",
"rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-node-resolve": "^5.2.0",
"standard": "^13.1.0", "semistandard": "^14.2.0",
"tape": "^4.11.0", "tape": "^4.11.0",
"tape-promise": "^4.0.0" "tape-promise": "^4.0.0"
}, },
"dependencies": { "dependencies": {
"@actions/core": "^1.2.0", "@actions/core": "^1.2.0",
"@actions/github": "^1.1.0" "@actions/github": "^1.1.0",
"nanoassert": "^2.0.0",
"qs": "^6.9.1"
},
"semistandard": {
"ignore": [
"dist"
]
} }
} }

View File

@ -1,6 +1,7 @@
import resolve from 'rollup-plugin-node-resolve' import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs' import commonjs from 'rollup-plugin-commonjs';
import builtins from 'builtin-modules';
export default [ export default [
{ {
@ -14,6 +15,7 @@ export default [
preferBuiltins: true preferBuiltins: true
}), }),
commonjs() commonjs()
] ],
external: builtins
} }
] ];

51
test.js
View File

@ -1,9 +1,44 @@
import tape from 'tape' import tape from 'tape';
import ptape from 'tape-promise' import ptape from 'tape-promise';
import { thing } from './main' import { readFileSync } from 'fs';
const test = ptape(tape) import { resolve } from 'path';
import { NeocitiesAPIClient } from './lib/client.js';
const test = ptape(tape);
test('a test', async t => { let token = process.env.NEOCITIES_API_TOKEN;
console.log(thing)
t.ok('pass') if (!token) {
}) try {
const config = JSON.parse(readFileSync(resolve(__dirname, 'config.json')));
token = config.token;
} catch (e) {
console.warn('error loading config.json');
console.warn(e);
}
}
if (token) {
test('token loaded', async t => {
t.ok(token);
});
test('basic client api', async t => {
const client = new NeocitiesAPIClient(token);
t.ok(client.info, 'info method available');
t.ok(client.list, 'list method available');
t.ok(client.get, 'get method available');
t.ok(client.post, 'post method available');
});
test('can get info about site', async t => {
const client = new NeocitiesAPIClient(token);
const info = await client.info();
console.log(info);
const list = await client.list();
console.log(list);
});
} else {
console.warn('No token set, live tests disabled');
}