diff --git a/README.md b/README.md index 584a646dd..cb59c19ba 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ snippets/ #### source -*Required* +*Required* Type: `object` Name of [conversion target](https://github.com/Mashape/httpsnippet/wiki/Targets) @@ -87,7 +87,7 @@ var snippet = new HTTPSnippet({ #### target -*Required* +*Required* Type: `string` Name of [conversion target](https://github.com/Mashape/httpsnippet/wiki/Targets) @@ -119,7 +119,7 @@ console.log(snippet.convert('node', { #### target -*Required* +*Required* Type: `string` Name of [conversion target](https://github.com/Mashape/httpsnippet/wiki/Targets) @@ -153,6 +153,39 @@ console.log(snippet.convert('shell', 'curl', { console.log(snippet.convert('node', 'unirest')); ``` +### addTarget(target) +#### target + +*Required* +Type: `object` + +Representation of a [conversion target](https://github.com/Kong/httpsnippet/wiki/Creating-Targets). Can use this to use targets that are not officially supported. + +```js +const customLanguageTarget = require('httpsnippet-for-my-lang'); +HTTPSnippet.addTarget(customLanguageTarget); +``` + +### addTargetClient(target, client) +### target + +*Required* +Type: `string` + +Name of [conversion target](https://github.com/Mashape/httpsnippet/wiki/Targets) + +### client + +*Required* +Type: `object` + +Representation of a [conversion target client](https://github.com/Kong/httpsnippet/wiki/Creating-Targets). Can use this to use target clients that are not officially supported. + +```js +const customClient = require('httpsnippet-for-my-node-http-client'); +HTTPSnippet.addTargetClient('node', customClient); +``` + ## Documentation At the heart of this module is the [HAR Format](http://www.softwareishard.com/blog/har-12-spec/#request) as the HTTP request description format, please review some of the sample JSON HAR Request objects in [test fixtures](/test/fixtures/requests), or read the [HAR Docs](http://www.softwareishard.com/blog/har-12-spec/#request) for more details. diff --git a/src/index.js b/src/index.js index c50e35347..815593611 100644 --- a/src/index.js +++ b/src/index.js @@ -221,6 +221,32 @@ HTTPSnippet.prototype._matchTarget = function (target, client) { // exports module.exports = HTTPSnippet +module.exports.addTarget = function (target) { + if (!('info' in target)) { + throw new Error('The supplied custom target must contain an `info` object.') + } else if (!('key' in target.info) || !('title' in target.info) || !('extname' in target.info) || !('default' in target.info)) { + throw new Error('The supplied custom target must have an `info` object with a `key`, `title`, `extname`, and `default` property.') + } else if (targets.hasOwnProperty(target.info.key)) { + throw new Error('The supplied custom target already exists.') + } else if (Object.keys(target).length === 1) { + throw new Error('A custom target must have a client defined on it.') + } + + targets[target.info.key] = target +} + +module.exports.addTargetClient = function (target, client) { + if (!targets.hasOwnProperty(target)) { + throw new Error(`Sorry, but no ${target} target exists to add clients to.`) + } else if (!('info' in client)) { + throw new Error('The supplied custom target client must contain an `info` object.') + } else if (!('key' in client.info) || !('title' in client.info)) { + throw new Error('The supplied custom target client must have an `info` object with a `key` and `title` property.') + } + + targets[target][client.info.key] = client +} + module.exports.availableTargets = function () { return Object.keys(targets).map(function (key) { var target = Object.assign({}, targets[key].info) diff --git a/test/fixtures/customTarget.js b/test/fixtures/customTarget.js new file mode 100644 index 000000000..9a6e11c41 --- /dev/null +++ b/test/fixtures/customTarget.js @@ -0,0 +1,12 @@ +'use strict' + +module.exports = { + info: { + key: 'js-variant', + title: 'JavaScript Variant', + extname: '.js', + default: 'request' + }, + + request: require('../../src/targets/node/request') +} diff --git a/test/targets.js b/test/targets.js index 319dbe0e8..675293540 100644 --- a/test/targets.js +++ b/test/targets.js @@ -1,4 +1,4 @@ -/* global describe, it */ +/* global describe, it, beforeEach */ 'use strict' @@ -85,15 +85,105 @@ var itShouldGenerateOutput = function (request, path, target, client) { } describe('Available Targets', function () { - var targets = HTTPSnippet.availableTargets() - - targets.forEach(function (target) { + HTTPSnippet.availableTargets().forEach(function (target) { it('available-targets.json should include ' + target.title, function () { fixtures['available-targets'].should.containEql(target) }) }) }) +describe('Custom targets', function () { + describe('Adding a custom target', function () { + it('should throw if the target does has no info object', function () { + (function () { + HTTPSnippet.addTarget({}) + }).should.throw(Error) + }) + + it('should throw if the target does not have a properly constructed info object', function () { + (function () { + HTTPSnippet.addTarget({info: {key: ''}}) + }).should.throw(Error) + }) + + it('should throw if the target already exists', function () { + (function () { + HTTPSnippet.addTarget(targets.node) + }).should.throw(Error) + }) + + it('should throw if the target has no client', function () { + (function () { + HTTPSnippet.addTarget({ + info: targets.node.info + }) + }).should.throw(Error) + }) + + it('should add and convert for a new custom target', function () { + const customTarget = require('./fixtures/customTarget') + + HTTPSnippet.addTarget(customTarget) + const target = HTTPSnippet.availableTargets().find(function (target) { return target.key === customTarget.info.key }) + const client = target.clients.find(function (client) { return client.key === customTarget.info.default }) + client.should.be.an.Object() + + Object.keys(fixtures.requests).filter(clearInfo).forEach(function (request) { + // Re-using the `request` module fixtures and framework since we copied it to create a custom client. + itShouldGenerateOutput(request, 'node/request/', customTarget.info.key, customTarget.info.default) + }) + }) + }) + + describe('Adding a custom client target', function () { + let customClient + + beforeEach(function () { + // Re-using the existing request client instead of mocking out something completely new. + customClient = { + ...targets.node.request, + info: { + key: 'axios', + title: 'Axios', + link: 'https://www.npmjs.com/package/axios', + description: 'Promise based HTTP client for the browser and node.js' + } + } + }) + + it("should throw if the client's target does not exist", function () { + (function () { + HTTPSnippet.addTargetClient('node.js', customClient) + }).should.throw(Error) + }) + + it('should throw if the client does has no info object', function () { + (function () { + HTTPSnippet.addTargetClient('node', {}) + }).should.throw(Error) + }) + + it('should throw if the target does not have a properly constructed info object', function () { + (function () { + HTTPSnippet.addTargetClient('node', {info: {key: ''}}) + }).should.throw(Error) + }) + + it('should add and convert for a new custom client target', function () { + HTTPSnippet.addTargetClient('node', customClient) + + const target = HTTPSnippet.availableTargets().find(function (target) { return target.key === 'node' }) + const client = target.clients.find(function (client) { return client.key === customClient.info.key }) + client.should.be.an.Object() + + Object.keys(fixtures.requests).filter(clearInfo).forEach(function (request) { + // Re-using the `request` module fixtures and framework since we copied it to create a custom client target. + itShouldGenerateOutput(request, 'node/request/', 'node', customClient.info.key) + }) + }) + }) +}) + // test all the things! describe('Targets', function () { Object.keys(targets).forEach(function (target) {