Skip to content
This repository has been archived by the owner on Nov 8, 2024. It is now read-only.

Detecting of input format #42

Merged
merged 2 commits into from
Jun 13, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ dt.compile('# My API\n...', 'apiary.apib', function (error, compilationResult) {

Result of compilation. Alongside compiled [Transaction][transaction-object-spec] objects contains also errors and warnings, mainly from API description parser.

- `mediaType`: `text/vnd.apiblueprint` (string, default, nullable) - Media type of the input format, defaults to API Blueprint format. Can be empty in case of some fatal errors.
- `transactions` (array[[Transaction][transaction-object-spec]]) - Compiled _HTTP Transactions_.
- `errors` (array[[Annotation][annotation-object-spec]]) - Errors which occurred during parsing of the API description or during compilation of transactions.
- `warnings` (array[[Annotation][annotation-object-spec]]) - Warnings which occurred during parsing of the API description or during compilation of transactions.
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dredd-transactions",
"version": "1.2.1",
"version": "1.3.0",
"description": "Compiles HTTP Transactions (Request-Response pairs) from API description document",
"main": "lib/dredd-transactions.js",
"scripts": {
Expand All @@ -18,6 +18,7 @@
},
"dependencies": {
"clone": "^1.0.2",
"deckardcain": "^0.3.2",
"fury": "^2.1.0",
"fury-adapter-apib-parser": "^0.2.0",
"fury-adapter-swagger": "^0.8.0-pre.12",
Expand Down
18 changes: 11 additions & 7 deletions src/dredd-transactions.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,32 @@ compile = (input, filename, callback) ->
# should be returned in the "compilation result". Callback should get
# an error only in case of unexpected crash.

parse(input, (err, apiElements) ->
parse(input, (err, parseResult) ->
# If 'apiElements' isn't empty, then we don't need to care about 'err'
# as it should be represented by annotation inside 'apiElements'
# and compilation should be able to deal with it and propagate it.
if err and not apiElements
compilationResult = {transactions: [], warnings: [], errors: [
component: 'apiDescriptionParser'
message: err.message
]}
return callback(null, compilationResult)
if err and not parseResult
return callback(null,
mediaType: null
transactions: []
warnings: []
errors: [{component: 'apiDescriptionParser', message: err.message}]
)

# The try/catch is just to deal with unexpected crash. Compilation passes
# all errors as part of the 'result' and it should not throw anything
# in any case.
try
{mediaType, apiElements} = parseResult
result = compileFromApiElements(apiElements, filename)
catch err
return callback(err)

for transaction in result.transactions
transaction['name'] = getTransactionName(transaction)
transaction['path'] = getTransactionPath(transaction)

result.mediaType = mediaType
callback(null, result)
)

Expand Down
54 changes: 27 additions & 27 deletions src/parse.coffee
Original file line number Diff line number Diff line change
@@ -1,44 +1,44 @@

deckardcain = require('deckardcain')
fury = require('fury')
fury.use(require('fury-adapter-apib-parser'))
fury.use(require('fury-adapter-swagger'))


furyParse = (source, options, callback) ->
[callback, options] = [options, {}] if typeof options is 'function'
createAnnotation = (message, type) ->
{
element: 'annotation'
meta: {classes: [type]}
content: message
}

args = {source, generateSourceMap: true}
args.mediaType = 'text/vnd.apiblueprint' if options.forceApiBlueprint

fury.parse(args, (err, result) ->
if not (err or result)
parse = (source, callback) ->
annotations = []
mediaType = deckardcain.identify(source)

unless mediaType
mediaType = 'text/vnd.apiblueprint'
annotations.push(createAnnotation('''\
Could not recognize API description format. \
Falling back to API Blueprint by default.\
''', 'warning'))

args = {source, mediaType, generateSourceMap: true}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need source maps? I didn't realise dredd is using this option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To detect transaction examples (see test/unit/detect-transaction-examples-test.coffee) which were present in AST but aren't used in API Elements, but Dredd uses them for addressing transactions in hooks (to be changed with apiaryio/dredd#227).

fury.parse(args, (err, apiElements) ->
if not (err or apiElements)
err = new Error('Unexpected parser error occurred.')
else if err
# Turning Fury error object into standard JavaScript error
err = new Error(err.message)

# If no parse result is present, indicate that with 'null',
# not with 'undefined'.
callback(err, (if result then result.toRefract() else null))
)


parse = (source, callback) ->
furyParse(source, (err, result) ->
if err and err.message.match(/document.+match.+registered.+parser/i)
annotation =
element: 'annotation'
meta: {classes: ['warning']}
content: "#{err.message} Falling back to API Blueprint by default."

# Fury wasn't able to recognize document format, falling back
# to API Blueprint
furyParse(source, {forceApiBlueprint: true}, (err, result) ->
result.content.push(annotation) if result
callback(err, result)
)
if apiElements
apiElements = apiElements.toRefract()
apiElements.content = apiElements.content.concat(annotations)
else
callback(err, result)
apiElements = null

callback(err, {mediaType, apiElements})
)


Expand Down
23 changes: 15 additions & 8 deletions test/schemas/compilation-result.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ module.exports = (options = {}) ->
warnings = options.warnings or 0
transactions = options.transactions or 0

# Either false (= no media type property should be present) or it
# will default to true (= structure must contain media type property)
mediaType = if options.mediaType is false then false else true

# Either false (= no transaction names and paths should be present) or it
# will default to true (= structure must contain transaction names and paths)
paths = if options.paths is false then false else true
Expand Down Expand Up @@ -75,16 +79,19 @@ module.exports = (options = {}) ->
transactionSchema.properties.path = {type: 'string'}
transactionSchema.required.push('path')

transactionsSchema = addMinMax({type: 'array', items: transactionSchema}, transactions)
errorsSchema = addMinMax({type: 'array'}, errors)
warningsSchema = addMinMax({type: 'array'}, warnings)
properties =
transactions: addMinMax({type: 'array', items: transactionSchema}, transactions)
errors: addMinMax({type: 'array'}, errors)
warnings: addMinMax({type: 'array'}, warnings)
required = ['transactions', 'errors', 'warnings']

if mediaType
properties.mediaType = {anyOf: [{type: 'string'}, {type: 'null'}]}
required.push('mediaType')

{
type: 'object'
properties:
transactions: transactionsSchema
errors: errorsSchema
warnings: warningsSchema
required: ['transactions', 'errors', 'warnings']
properties
required
additionalProperties: false
}
1 change: 1 addition & 0 deletions test/unit/compilation/compile-test.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ describe('compile() · all API description formats', ->
filename
transactions: true
paths: false
mediaType: false
})

fixtures.ordinary.forEachDescribe(({source}) ->
Expand Down
Loading