Skip to content

Commit

Permalink
Merge pull request #165 from readmeio/feat/plugin-architecture
Browse files Browse the repository at this point in the history
Adding the ability to import custom targets and target clients
  • Loading branch information
reynolek committed Jun 2, 2020
2 parents 13dd4fd + b7818e6 commit 7755339
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 7 deletions.
39 changes: 36 additions & 3 deletions README.md
Expand Up @@ -69,7 +69,7 @@ snippets/

#### source

*Required*
*Required*
Type: `object`

Name of [conversion target](https://github.com/Mashape/httpsnippet/wiki/Targets)
Expand All @@ -87,7 +87,7 @@ var snippet = new HTTPSnippet({

#### target

*Required*
*Required*
Type: `string`

Name of [conversion target](https://github.com/Mashape/httpsnippet/wiki/Targets)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down
26 changes: 26 additions & 0 deletions src/index.js
Expand Up @@ -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)
Expand Down
12 changes: 12 additions & 0 deletions 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')
}
98 changes: 94 additions & 4 deletions test/targets.js
@@ -1,4 +1,4 @@
/* global describe, it */
/* global describe, it, beforeEach */

'use strict'

Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 7755339

Please sign in to comment.