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

Commit

Permalink
Merge pull request #64 from apiaryio/wvi/validate
Browse files Browse the repository at this point in the history
feat. Add `validate` function to the API
  • Loading branch information
pksunkara authored Oct 31, 2016
2 parents 29367be + 96d227d commit 5cb87ce
Show file tree
Hide file tree
Showing 10 changed files with 340 additions and 36 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# drafter.js Changelog

## 2.6.0

### Enhancements

- Added `validate` and `validateSync` to just return the warnings and errors
after parsing a blueprint.

## 2.5.2

This update now uses Drafter 3.1.3. Please see [Drafter
Expand Down
46 changes: 40 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,14 @@ HTML script tag.
Once you've included drafter.js, you can parse an API Blueprint:

```javascript
try {
var res = drafter.parse('# API Blueprint...', {exportSourcemap: true});
console.log(res);
} catch (err) {
console.log(err);
}

var res = drafter.parse('# API Blueprint...', {exportSourcemap: true}, function (err, res) {
if (err) {
console.log(err)
}
console.log(res);
});

```

Supported options:
Expand All @@ -73,6 +75,38 @@ Supported options:
missing a title.
- `type`: Either `refract` (default) or `ast`.

Or if you want just to validate it and are insterested only in parsing
errors and warnings:

```javascript
var res = drafter.validate('# API Blueprint...', {requireBlueprintname: true}, function (err, res) {
if (err) {
console.log(err)
}

if (res) {
console.log("Document has semantic issues!");
console.log(res);
} else {
console.log("Document is valid with no warnings.");
}
});
```

Supported options:

- `json`: Set to `false` to disable parsing of the JSON data. You will
instead get a JSON string as the result.
- `requireBlueprintName`: Set to generate an error if the blueprint is
missing a title.

#### Synchronous API

Both functions have their synchronous counterpart which instead of callback return the result and in case of error throw exception.

- `parseSync(source, options)`
- `validateSync(source, options)`

### Build drafter.js

*Unfortunately building drafter.js works only on a *nix environment at the
Expand Down
2 changes: 1 addition & 1 deletion ext/drafter
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "drafter.js",
"version": "2.5.2",
"version": "2.6.0",
"description": "Pure JS Drafter built with Emscripten",
"main": "lib/drafter.nomem.js",
"scripts": {
"test": "node scripts/test.js",
"test": "node scripts/test.js && node scripts/test-validate.js",
"build": "./scripts/wrap.js && docker run --rm -v $(pwd):/src -t apiaryio/emcc:1.36 scripts/emcbuild.sh",
"clean": "docker run --rm -v $(pwd):/src -t apiaryio/emcc:1.36 scripts/emcclean.sh",
"release": "scripts/release.sh"
Expand Down
8 changes: 4 additions & 4 deletions scripts/emcbuild.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ em++ $FLAGS "build/out/$BUILD_TYPE/libdrafterjs.a" \
"$DRAFTER_PATH/build/out/$BUILD_TYPE/libsnowcrash.a" \
"$DRAFTER_PATH/build/out/$BUILD_TYPE/libsundown.a" \
"$DRAFTER_PATH/build/out/$BUILD_TYPE/libmarkdownparser.a" \
-s EXPORTED_FUNCTIONS="['_c_parse']" \
-s EXPORTED_FUNCTIONS="['_c_parse', '_c_validate']" \
-s DISABLE_EXCEPTION_CATCHING=0 \
-s EXPORTED_RUNTIME_METHODS="['writeStringToMemory', 'getValue', 'Pointer_stringify', 'lengthBytesUTF8', 'UTF8ToString']" \
-s EXPORTED_RUNTIME_METHODS="['stringToUTF8', 'getValue', 'Pointer_stringify', 'lengthBytesUTF8', 'UTF8ToString']" \
-s ASSERTIONS=${ASSERT} \
-s DOUBLE_MODE=0 \
-s ALLOW_MEMORY_GROWTH=1 \
Expand All @@ -107,9 +107,9 @@ em++ $FLAGS --memory-init-file 0 \
"$DRAFTER_PATH/build/out/$BUILD_TYPE/libsnowcrash.a" \
"$DRAFTER_PATH/build/out/$BUILD_TYPE/libsundown.a" \
"$DRAFTER_PATH/build/out/$BUILD_TYPE/libmarkdownparser.a" \
-s EXPORTED_FUNCTIONS="['_c_parse']" \
-s EXPORTED_FUNCTIONS="['_c_parse', '_c_validate']" \
-s DISABLE_EXCEPTION_CATCHING=0 \
-s EXPORTED_RUNTIME_METHODS="['writeStringToMemory', 'getValue', 'Pointer_stringify', 'lengthBytesUTF8', 'UTF8ToString']" \
-s EXPORTED_RUNTIME_METHODS="['stringToUTF8', 'getValue', 'Pointer_stringify', 'lengthBytesUTF8', 'UTF8ToString']" \
-s ASSERTIONS=${ASSERT} \
-s ALLOW_MEMORY_GROWTH=1 \
-s NO_EXIT_RUNTIME=1 \
Expand Down
26 changes: 26 additions & 0 deletions scripts/fixtures/fail.apib
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
FORMAT: 1A
HOST: https://example.org

# Test-API

description

# Foo [/foo/bar]

Get data

## Retrieve the data [GET]

+ Request

+ Response (application/vnd.api+json)

+ Attributes

+ foo1: `barä` (string)
+ foo2: `barö` (string)
+ foo3: `barü` (string)
+ foo4: `barß` (string)
+ foo5: `bar` (string) - This is also bad ä
+ fö: `bar` (string) - This too

140 changes: 140 additions & 0 deletions scripts/test-validate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#!/usr/bin/env node

var assert = require('chai').assert;
var async = require('async');
var chalk = require('chalk');
var exec = require('child_process').exec;
var fs = require('fs');
var glob = require('glob');
var jsdiff = require('diff');
var path = require('path');
var drafter = require('../lib/drafter.nomem.js');
var protagonist = require('protagonist');

var testRun = {
total: 0,
pass: 0,
fail: 0,
jsTime: 0,
prtgTime: 0,
cppTime: 0
};

var options = {};

/*
* Convert a duration from `process.hrtime()` into milliseconds.
*/
function ms(duration) {
return duration[0] * 1000 + duration[1] / 1e6;
}

/*
* Tests a single file against the output of protagonist
*/
function testFile(filename, done) {
console.log(filename);
testRun.total++;
async.series({
protagonist: function (callback) {
fs.readFile(filename, 'utf8', function (err, data) {
var start = process.hrtime();
protagonist.validate(data, options, function (error, result) {
var duration = process.hrtime(start);
callback(null, {
output: result,
duration: ms(duration),
error: error
});
});
});
},
js: function (callback) {
fs.readFile(filename, 'utf8', function (err, data) {
var start = process.hrtime();
drafter.validate(data, options, function (error, result) {
var duration = process.hrtime(start);
callback(null, {
output: result,
duration: ms(duration),
error: error
});
});
});
}
}, function (err, results) {
// Parsing both ways has completed. Now we compare them!
if (err) return done(err);

try {
if (!results.js.error && !results.protagonist.error) {
assert.deepEqual(results.js.output, results.protagonist.output, 'Parsed correctly as expected');
} else if (results.js.error && results.protagonist.error) {
if (results.js.error.result) {
assert.deepEqual(results.js.error.result, results.protagonist.error.result, 'JS and Protagonist both failed.');
} else{
throw Error("JS without result: " + JSON.stringify(results.js.error));
}
} else if (results.js.error) {
console.log(results.protagonist.error);
console.log(results.protagonist.output);
throw Error("JS parsing failed: " + JSON.stringify(results.js.error));
} else {
throw Error("Protagonist parsing failed: " + JSON.stringify(results.protagonist.error));
}

var durationDiff = results.js.duration / results.protagonist.duration;
console.log('OK ' + filename + ' JS:' + parseInt(results.js.duration) + 'ms P:' + parseInt(results.protagonist.duration) + 'ms');
testRun.pass++;
} catch (err) {
console.log('FAIL ' + filename);

if (!results.js.ouptut) {
console.log(chalk['red'](err));
} else {
// Get a smart diff and display only the parts that have changed.
var diff = jsdiff.diffJson(results.js.output, results.protagonist.output);
if (!diff.length) {
console.log(err);
}
diff.forEach(function (part) {
if (part.added || part.removed) {
var color = part.added ? 'green' : 'red';
console.log(chalk[color](part.value));
}
});
}
testRun.fail++;
}

testRun.prtgTime += results.protagonist.duration;
testRun.jsTime += results.js.duration;

done();
});
}


/*
* Loop through all the files, test them, then print a report.
*/
fixtures = [].concat(
glob.sync('ext/drafter/test/**/*.apib'),
glob.sync('scripts/fixtures/*.apib')
);

async.eachLimit(fixtures, 1, testFile, function (err) {
if (err) {
console.log(err);
console.log();
}

console.log('\nTest run result:\n================');
console.log('Total: ' + testRun.total);
console.log('Passed: ' + testRun.pass);
console.log('Failed: ' + testRun.fail);
console.log('Average JS speed: ' + (testRun.jsTime / testRun.prtgTime).toFixed(1) + ' times slower than Protagonist');

process.exit(testRun.fail > 0 ? -1 : 0);
});

21 changes: 21 additions & 0 deletions src/cparse.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "cparse.h"

#include "snowcrash.h"
#include "drafter_private.h"
#include "sosJSON.h"

#include "SerializeAST.h"
Expand Down Expand Up @@ -56,3 +57,23 @@ SC_API int c_parse(const char* source,

return blueprint.report.error.code;
}

SC_API int c_validate(const char *source,
sc_blueprint_parser_options options,
char **result)
{
drafter_result *res = NULL;
drafter_parse_options parse_opts;
parse_opts.requireBlueprintName = (bool)(options & SC_REQUIRE_BLUEPRINT_NAME_OPTION);

res = drafter_check_blueprint_with_options(source, parse_opts);

if (NULL != res) {
*result = drafter_serialize(res,
(drafter_options){false, DRAFTER_SERIALIZE_JSON});
drafter_free_result(res);
return 1;
}

return 0;
}
10 changes: 7 additions & 3 deletions src/cparse.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@ enum drafter_ast_type_option {
};

SC_API int c_parse(const char* source,
sc_blueprint_parser_options options,
enum drafter_ast_type_option astType,
char** result);
sc_blueprint_parser_options options,
enum drafter_ast_type_option astType,
char** result);

SC_API int c_validate(const char *source,
sc_blueprint_parser_options options,
char **result);

#ifdef __cplusplus
}
Expand Down
Loading

0 comments on commit 5cb87ce

Please sign in to comment.