Merge pull request #1 from Seich/defaults

Added Defaults.
This commit is contained in:
Sergio Díaz 2017-12-27 16:47:14 -06:00 committed by GitHub
commit dc7d6f977d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 2229 additions and 358 deletions

View File

@ -4,7 +4,11 @@
<h1 align="center">Beau</h1>
<p align="center">Testing JSON APIs made easy.</p>
<p align="center">
<a href="https://codeclimate.com/github/Seich/Beau/maintainability"><img src="https://api.codeclimate.com/v1/badges/bc2de4d71893d6a2d18b/maintainability" /></a>
<a href="https://codeclimate.com/github/Seich/Beau/test_coverage"><img src="https://api.codeclimate.com/v1/badges/bc2de4d71893d6a2d18b/test_coverage" /></a>
<a href="https://circleci.com/gh/Seich/Beau/tree/master"><img src="https://circleci.com/gh/Seich/Beau/tree/master.svg?style=svg" alt="CircleCI"></a>
</p>
## What is Beau?
Beau, is a CLI that executes HTTP requests based on a YAML configuration file. This makes testing easy, it allows you to share test requests with others as part of your repo.
@ -33,7 +37,7 @@ Beau, is a CLI that executes HTTP requests based on a YAML configuration file. T
## Example Configuration File
version: 1
host: https://example.com/api/
endpoint: https://example.com/api/
POST /session:
ALIAS: session

View File

@ -0,0 +1,96 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Beau's config Loader. should set up defaults for all requests 1`] = `
Beau {
"config": Object {
"CACHE": false,
"DEFAULTS": Object {
"headers": Object {
"authentication": "hello",
},
},
"ENDPOINT": "http://jsonplaceholder.typicode.com",
"PLUGINS": Array [],
"VERSION": 1,
},
"configKeys": Array [
"VERSION",
"CACHE",
"ENDPOINT",
"PLUGINS",
"DEFAULTS",
],
"defaults": Object {
"CACHE": false,
"DEFAULTS": Object {
"headers": Object {
"authentication": "hello",
},
},
"ENDPOINT": "http://jsonplaceholder.typicode.com",
"PLUGINS": Array [],
"VERSION": 1,
},
"requests": RequestList {
"cache": RequestCache {
"$cache": Object {},
},
"config": Object {
"CACHE": false,
"DEFAULTS": Object {
"headers": Object {
"authentication": "hello",
},
},
"ENDPOINT": "http://jsonplaceholder.typicode.com",
"PLUGINS": Array [],
"VERSION": 1,
},
"list": Array [
Request {
"ALIAS": "get-post",
"DEPENDENCIES": Set {},
"DOCUMENTATION": undefined,
"ENDPOINT": "http://jsonplaceholder.typicode.com/posts/1",
"HEADERS": Object {
"authentication": "hello",
},
"PARAMS": undefined,
"PAYLOAD": undefined,
"VERB": "GET",
"originalRequest": Object {
"ALIAS": "get-post",
"ENDPOINT": "http://jsonplaceholder.typicode.com",
"headers": Object {
"authentication": "hello",
},
"request": "GET /posts/1",
},
},
Request {
"ALIAS": "user",
"DEPENDENCIES": Set {},
"DOCUMENTATION": undefined,
"ENDPOINT": "http://jsonplaceholder.typicode.com/user",
"HEADERS": Object {
"authentication": "hello",
"hello": "world",
},
"PARAMS": undefined,
"PAYLOAD": undefined,
"VERB": "GET",
"originalRequest": Object {
"ENDPOINT": "http://jsonplaceholder.typicode.com",
"alias": "user",
"headers": Object {
"authentication": "hello",
"hello": "world",
},
"request": "GET /user",
},
},
],
"modifiers": Array [],
},
}
`;

View File

@ -48,7 +48,7 @@ Array [
},
Object {
"alias": "show",
"host": "http://martianwabbit.com",
"endpoint": "http://martianwabbit.com",
"request": "GET /user",
},
],

View File

@ -1,23 +1,44 @@
const Beau = require('../beau');
const yaml = require('js-yaml');
const Beau = require('../beau');
describe(`Beau's config Loader.`, () => {
it('Should only load valid configuration keys', () => {
let host = 'http://martianwabbit.com';
let version = 1;
let cache = false;
let shouldntBeAdded = true;
it('Should only load valid configuration keys', () => {
const doc = yaml.safeLoad(`
version: 1
endpoint: http://martianwabbit.com
cache: false
shouldntBeAdded: true
`);
let beau = new Beau({
version,
host,
cache,
shouldntBeAdded
});
const beau = new Beau(doc);
expect(beau.config.HOST).toBe(host);
expect(beau.config.CACHE).toBe(cache);
expect(beau.config.VERSION).toBe(version);
expect(beau.config.shouldntBeAdded).toBeUndefined();
});
expect(beau.config.ENDPOINT).toBe(doc.endpoint);
expect(beau.config.CACHE).toBe(doc.cache);
expect(beau.config.VERSION).toBe(doc.version);
expect(beau.config.shouldntBeAdded).toBeUndefined();
});
it('should set up defaults for all requests', () => {
const doc = yaml.safeLoad(`
version: 1
endpoint: 'http://jsonplaceholder.typicode.com'
defaults:
headers:
authentication: hello
GET /posts/1: get-post
GET /user:
alias: user
headers:
hello: world
`);
const beau = new Beau(doc);
expect(beau).toMatchSnapshot();
beau.requests.list.forEach(r => {
expect(r.HEADERS.authentication).toMatch('hello');
});
});
});

View File

@ -12,7 +12,7 @@ describe('Request', () => {
beforeEach(() => {
req = {
request: 'POST /user',
host: 'http://martianwabbit.com',
endpoint: 'http://martianwabbit.com',
alias: 'update',
params: {
userId: '$profile.UserId'
@ -31,7 +31,7 @@ describe('Request', () => {
request = new Request(req);
requestWithoutDependencies = new Request({
host: 'http://martianwabbit.com',
endpoint: 'http://martianwabbit.com',
request: 'GET /user',
alias: 'show'
});
@ -41,7 +41,7 @@ describe('Request', () => {
test('It should load up the given request', () => {
expect(request.VERB).toBe('POST');
expect(request.ENDPOINT).toBe(req.host + '/user');
expect(request.ENDPOINT).toBe(req.endpoint + '/user');
expect(request.HEADERS).toBeDefined();
expect(request.PAYLOAD).toBeDefined();
expect(request.PARAMS).toBeDefined();

View File

@ -2,11 +2,11 @@ const RequestList = require('../requestList');
const requestPromiseNativeMock = require('request-promise-native');
describe('RequestList', () => {
const host = 'http://martianwabbit.com';
const endpoint = 'http://martianwabbit.com';
const doc = {
'POST /session': null,
'Not a Request': null,
'GET /post': 'get-posts',
'GET /post': { alias: 'get-posts' },
'POST /user': {
alias: 'user',
payload: {
@ -20,7 +20,7 @@ describe('RequestList', () => {
beforeEach(() => {
requestPromiseNativeMock.fail = false;
requests = new RequestList(doc, {
HOST: host,
ENDPOINT: endpoint,
PLUGINS: [
{
'beau-jwt': {
@ -40,7 +40,7 @@ describe('RequestList', () => {
expect(requests.list.length).toBe(3);
expect(request.VERB).toBe('POST');
expect(request.ENDPOINT).toBe(host + '/session');
expect(request.ENDPOINT).toBe(endpoint + '/session');
});
it('should fetch dependencies', () => {

32
beau.js
View File

@ -1,17 +1,43 @@
const deepMerge = require('deepmerge');
const RequestList = require('./requestList');
const requestRegex = require('./shared').requestRegex;
class Beau {
constructor(doc) {
this.defaults = {
VERSION: 1,
CACHE: false,
HOST: '',
PLUGINS: []
ENDPOINT: '',
PLUGINS: [],
DEFAULTS: []
};
this.configKeys = Object.keys(this.defaults);
this.config = this.loadConfig(doc);
this.requests = new RequestList(doc, this.config);
this.requests = this.getRequests(doc);
this.requests = new RequestList(this.requests, this.config);
}
getRequests(doc) {
let requests = Object.keys(doc).filter(key => {
return requestRegex.test(key);
});
let results = {};
requests.forEach(r => {
if (typeof doc[r] === 'string') {
results[r] = {
ALIAS: doc[r]
};
} else {
results[r] = doc[r];
}
results[r] = deepMerge(this.config.DEFAULTS, results[r]);
});
return results;
}
loadConfig(doc) {

12
circle.yml Normal file
View File

@ -0,0 +1,12 @@
machine:
node:
version: 8.9.3
dependencies:
post:
- npm install -g codeclimate-test-reporter
test:
pre:
- npm run test:coverage
- codeclimate-test-reporter < ./coverage/lcov.info

View File

@ -1,5 +1,5 @@
VERSION: 1
HOST: https://api.github.com
ENDPOINT: https://api.github.com
auth: &auth
HEADERS:

View File

@ -1,13 +1,16 @@
version: 1
host: 'http://jsonplaceholder.typicode.com'
endpoint: 'http://jsonplaceholder.typicode.com'
defaults:
headers:
hello: $posts.body.0.userId
GET /posts/1: get-post
GET /posts/:
alias: posts
documentation:
title: Fetch Posts
description: Fetches all posts available.
headers:
hello: false
POST /posts/:
alias: new-post

View File

@ -1,4 +1,4 @@
host: http://localhost:10080
endpoint: http://localhost:10080
plugins:
- beau-jwt:

View File

@ -1,5 +1,5 @@
VERSION: '1'
HOST: https://slack.com/api
ENDPOINT: https://slack.com/api
auth: &auth
token: xoxp-139455775026-139455775090-147751461120-f224ed6ffee029869a0f138d0859e7d6

2321
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -7,12 +7,14 @@
"license": "MIT",
"scripts": {
"test": "jest",
"watch": "jest --watch"
"watch": "jest --watch",
"test:coverage": "jest --coverage"
},
"dependencies": {
"cli-color": "^1.1.0",
"clui": "^0.3.1",
"commander": "^2.12.2",
"deepmerge": "^2.0.1",
"js-yaml": "^3.7.0",
"jsome": "^2.3.26",
"request": "^2.83.0",
@ -29,5 +31,8 @@
},
"bin": {
"beau": "./bin/beau"
},
"engines": {
"node": ">=8.9.3"
}
}

View File

@ -14,15 +14,15 @@ class Request {
REQUEST,
ALIAS,
PAYLOAD,
HOST,
ENDPOINT,
PARAMS,
HEADERS,
DOCUMENTATION
} = config;
const { verb, endpoint } = this.parseRequest(REQUEST);
const { verb, path } = this.parseRequest(REQUEST);
this.VERB = verb;
this.ENDPOINT = HOST + endpoint;
this.ENDPOINT = ENDPOINT + path;
this.HEADERS = HEADERS;
this.PAYLOAD = PAYLOAD;
@ -39,11 +39,11 @@ class Request {
}
parseRequest(request) {
let parts = request.match(requestRegex);
const parts = request.match(requestRegex);
return {
verb: parts[1],
endpoint: parts[2]
path: parts[2]
};
}

View File

@ -51,17 +51,8 @@ class RequestList {
});
return requests.map(request => {
const type = typeof doc[request];
if (type === 'string') {
doc[request] = {
ALIAS: doc[request]
};
}
doc[request] = doc[request] || {};
doc[request].HOST = this.config.HOST;
doc[request].ENDPOINT = this.config.ENDPOINT;
doc[request].request = request;
return new Request(doc[request]);

View File

@ -11,7 +11,7 @@ const httpVerbs = [
];
const requestRegex = new RegExp(`(${httpVerbs.join('|')})\\s(.*)`, 'i');
const replacementRegex = /\$([a-zA-Z\.\d\-\_]*)/g;
const replacementRegex = /\$([a-zA-Z\.\d\-\_\/\\]*)/g;
module.exports = {
httpVerbs,