From ac85dc2b118974b6c8a8689fb1b00b5ea6760fe7 Mon Sep 17 00:00:00 2001 From: bkp7 Date: Wed, 24 Jan 2018 19:10:52 +0000 Subject: [PATCH] Removed api files and functionality out to a new repository. Linked to that new repo in npm (@bkp7/schema-js-api v0.0.5) which will need to be transferred to signalK ownership. Before merging, all refs to @bkp7 need to be changed to @signalk. --- bin/validate.js | 28 -- package-lock.json | 90 ++++- package.json | 9 +- scripts/processSchemaFiles.js | 594 +------------------------------- src/delta.js | 66 ---- src/fullsignalk.js | 260 -------------- src/index.js | 320 ----------------- test/fullsignalk.js | 191 ---------- test/jsonSamplesAndTestFiles.js | 2 +- test/schema-api.js | 73 ---- test/schemaReferences.js | 21 +- test/sources.js | 109 ------ 12 files changed, 103 insertions(+), 1660 deletions(-) delete mode 100755 bin/validate.js delete mode 100644 src/delta.js delete mode 100644 src/fullsignalk.js delete mode 100644 src/index.js delete mode 100644 test/fullsignalk.js delete mode 100644 test/schema-api.js delete mode 100644 test/sources.js diff --git a/bin/validate.js b/bin/validate.js deleted file mode 100755 index 977287133..000000000 --- a/bin/validate.js +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env node - -var JSONStream = require('JSONStream'); -var Transform = require('stream').Transform; - -var signalkSchema = require('../'); - -process.stdin.resume(); -process.stdin.setEncoding('utf8'); - - -function Validator(options) { - Transform.call(this, { - objectMode: true - }); -} - -require('util').inherits(Validator, Transform); - -Validator.prototype._transform = function(chunk, encoding, done) { - var validationResult = signalkSchema.validateFull(chunk); - if (!validationResult.valid) { - console.error(JSON.stringify(validationResult, null, 2)); - } - done(); -} - -process.stdin.pipe(JSONStream.parse()).pipe(new Validator()); diff --git a/package-lock.json b/package-lock.json index 328ef717b..b48a16f2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,9 +1,48 @@ { "name": "@signalk/signalk-schema", - "version": "1.0.3", + "version": "1.0.4", "lockfileVersion": 1, "requires": true, "dependencies": { + "@bkp7/schema-js-api": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@bkp7/schema-js-api/-/schema-js-api-0.0.5.tgz", + "integrity": "sha512-m2A30Y2MczpOdjAB6WVAS0c5mMkMfTr+7Vnku8RQOhqccko5OXJTlV+Bg2xWBCpjTV15Rjlx1GQVoip2/rargA==", + "dev": true, + "requires": { + "JSONStream": "0.7.4", + "axios": "0.17.1", + "deasync": "0.1.12", + "debug": "2.6.9", + "fs-extra": "5.0.0", + "json-schema-ref-parser": "3.3.1", + "lodash": "3.10.1", + "tv4": "1.3.0", + "tv4-formats": "2.2.2" + }, + "dependencies": { + "fs-extra": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", + "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "jsonfile": "4.0.0", + "universalify": "0.1.1" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11" + } + } + } + }, "JSONSelect": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/JSONSelect/-/JSONSelect-0.4.0.tgz", @@ -193,6 +232,16 @@ "integrity": "sha1-PYHKabR0seFlGHKLUcJP8Lvtxuk=", "dev": true }, + "axios": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.17.1.tgz", + "integrity": "sha1-LY4+XQvb1zJ/kbyBT1xXZg+Bgk0=", + "dev": true, + "requires": { + "follow-redirects": "1.4.1", + "is-buffer": "1.1.6" + } + }, "babel-cli": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-cli/-/babel-cli-6.26.0.tgz", @@ -1152,6 +1201,12 @@ "dev": true, "optional": true }, + "bindings": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", + "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE=", + "dev": true + }, "bl": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz", @@ -1461,6 +1516,16 @@ "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=", "dev": true }, + "deasync": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.12.tgz", + "integrity": "sha512-gpacYo8FBZh3INBp2KOtrQp9kCO5faHvOmEZx3/cZTr3Mm8/kAYs7/Ws3E3OAH0ApBNK6Y6N+7+Dka2Zn2Fldw==", + "dev": true, + "requires": { + "bindings": "1.2.1", + "nan": "2.8.0" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1738,6 +1803,26 @@ "locate-path": "2.0.0" } }, + "follow-redirects": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.4.1.tgz", + "integrity": "sha512-uxYePVPogtya1ktGnAAXOacnbIuRMB4dkvqeNz2qTtTQsuzSfbDolV+wMMKxAmCx0bLgAKLbBOkjItMbbkR1vg==", + "dev": true, + "requires": { + "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -3974,8 +4059,7 @@ "version": "2.8.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", - "dev": true, - "optional": true + "dev": true }, "node-uuid": { "version": "1.4.8", diff --git a/package.json b/package.json index 6b375a7b6..04e3edaca 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,8 @@ "description": "SignalK specification schema as an npm module with tests", "main": "dist/index.js", "scripts": { - "test": "babel src --out-dir dist --copy-files && mocha", - "prepublish": "npm run js:dist && npm run docs:keys", - "js:watch": "babel --watch src --out-dir dist --copy-files", - "js:dist": "babel src --out-dir dist --copy-files", + "test": "mocha", + "prepublish": "npm run docs:keys", "schema:version": "cross-var replace-in-file /https:\\/\\/signalk.org\\/specification\\/[\\w\\.]+\\/schemas\\//g https://signalk.org/specification/$npm_package_version/schemas/ ./**/*.j* --ignore=./package.json,./node_modules/** --isRegex --verbose", "schema:publish": "git checkout gh-pages && git checkout master -- schemas && cross-var mkdir $npm_package_version && cross-var git add $npm_package_version && cross-var git mv schemas/ $npm_package_version/ && git commit -m \"Schemas from master\" && git push", "docs:changelog": "github-changes -f gitbook-docs/changelog.md -o signalk -r specification -a --only-pulls --use-commit-body --data=pulls", @@ -64,6 +62,7 @@ "mocha": "^2.1.0", "mz": "^2.4.0", "replace-in-file": "^3.1.0", - "rimraf": "^2.5.4" + "rimraf": "^2.5.4", + "@bkp7/schema-js-api": "0.0.5" } } diff --git a/scripts/processSchemaFiles.js b/scripts/processSchemaFiles.js index 8cdc0e3a2..4acfef880 100755 --- a/scripts/processSchemaFiles.js +++ b/scripts/processSchemaFiles.js @@ -1,594 +1,14 @@ #!/usr/bin/env node 'use strict' -/* - * Copyright 2016 Fabian Tollenaar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ - -const _ = require('lodash') +const skApi = require('@bkp7/schema-js-api') const fs = require('mz/fs') const path = require('path') -const rimraf = require('rimraf') -const markdown = new (require('markdown-it'))() -const RefParser = require('json-schema-ref-parser') - - -const units = { - "A": "Ampere", - "C": "Coulomb", - "Hz": "Hertz", - "ISO-8601 (UTC)": "ISO-8601 string representation of time in Universal Time Coordinated", - "J": "Joule", - "K": "Kelvin", - "Pa": "Pascal", - "V": "Volt", - "W": "Watt", - "deg": "Degree", - "kg": "Kilogram", - "m": "Meter", - "m/s": "Meters per second", - "m2": "Square meter", - "m3": "Cubic meter", - "m3/s": "Cubic meter per second", - "rad": "Radian", - "rad/s": "Radian per second", - "ratio": "Ratio", - "s": "Second" -} - -class Parser { - constructor (opts) { - this.options = _.defaults(opts, { - entry: './schema/signalk.json', - output: './build', - debug: false, - cwd: process.cwd(), - encoding: 'utf-8', - done: () => {} - }) - - this.debug = () => {} - this.tree = {} - this.docs = {} - this.invalid = [] - - this.parseOptions() - this.parse() - } - - parse () { - this - .rm(this.options.output) // remove build directory - .then(() => fs.mkdir(this.options.output)) // create a new build directory - .then(() => fs.mkdir(path.join(this.options.output, 'html'))) - .then(() => { - return RefParser.dereference(this.options.entry) - }) - - /* - * Start parsing of properties at root (signalk.json) - */ - .then(root => { - return this.parseProperties('/', root) - }) - - /* - * If debug is set to true, write all paths to a JSON file. - */ - .then(result => { - if (this.options.debug === false) { - return result - } - - let keys = Object.keys(this.tree).sort() - - return fs - .writeFile(path.join(this.options.output, 'paths.json'), JSON.stringify(keys, null, 2), this.options.encoding) - .then(_ => { - this.debug(`Written a list of paths to ${path.join(this.options.output, 'paths.json')}.`) - return result - }) - }) - - /* - * If debug is set to true, write the raw tree to a JSON file. - */ - .then(result => { - if (this.options.debug === false) { - return result - } - - const safeTree = _.pick(this.tree, value => { - try { - JSON.stringify(value) - } catch(e) { - return false - } - return true - }) - - return fs - .writeFile(path.join(this.options.output, 'tree.json'), JSON.stringify(safeTree, null, 2), this.options.encoding) - .then(() => { - this.debug(`Written total tree to ${path.join(this.options.output, 'tree.json')}`) - return result - }) - }) - - /* - * Replace RegExp's in the path name with for readability and generate a documentation object for each file - */ - .then(result => { - Object.keys(this.tree).forEach(path => { - if (!_.isObject(this.tree[path])) { - return - } - - const splitpath = path.split('/') - const subtree = this.tree[path] - const node = splitpath[splitpath.length - 1] - - for (let i in splitpath) { - if (splitpath[i].indexOf('^') !== -1 || splitpath[i].indexOf('$') > 0 || splitpath[i].indexOf('*') !== -1) { - splitpath[i] = '' - } - } - - const skipFields = ['timestamp', '$source', 'source', '_attr', 'meta', 'pgn', 'sentence', 'value', 'values'] - const embeddedFields = - !this.tree[`${path}/timestamp`] ? {} : - _.pick(subtree.properties ? _.omit(subtree.properties || {}, skipFields) : {}, (value, key) => { - return !this.tree[`${path}/${key}/timestamp`] - }) - - const documentation = { - node: node, - path: path, - path_normalised: splitpath.join('/'), - regexp: false, - title: node, - subtitle: typeof subtree.title !== 'undefined' ? subtree.title : null, - type: typeof subtree.type !== 'undefined' ? subtree.type : null, - description: typeof subtree.description !== 'undefined' ? subtree.description : null, - example: typeof subtree.example !== 'undefined' ? subtree.example : null, - json: subtree, - embeddedFields: Object.keys(embeddedFields).length > 0 ? embeddedFields : undefined - } - if (subtree.enum) { - documentation.enum = subtree.enum - } - - if (node.indexOf('^') !== -1 || node.indexOf('$') > 0 || node.indexOf('*') !== -1) { - splitpath[splitpath.length - 1] = '' - documentation.regexp = true - } - - if (typeof subtree.type === 'undefined') { - this.invalid.push(splitpath.join('/')) - } - - this.docs[splitpath.join('/')] = documentation - }) - - return this.docs - }) - - /* - * Normalise the path name to use as file name and write a Markdown-formatted file to disk - */ - .then(() => { - const promises = Object.keys(this.docs).map(p => { - const doc = this.docs[p] - const fn = (`${p.replace(/\//g, '.')}`).replace(/<|>/g, '__').replace(/^\./, '') - - // return fs - // .writeFile(path.join(this.options.output, `${fn}.md`), this.generateMarkdown(doc), this.options.encoding) - // .then(() => { - return { - path: p, - name: `${fn}.md`, - file: path.join(this.options.output, `${fn}.md`) - } - // }) - }) - - return Promise.all(promises) - }) - - /* - * Generate an index in Markdown, pass on results of markdown file creation. - */ - .then(results => { - const filenames = {} - const filter = ['/timestamp', '/$source', '/source', '/_attr', '/meta', '/pgn', '/sentence', '/value', '/values'] - - results.forEach(result => { - filenames[result.name] = result.path - }) - - const keysWithMeta = {} - - let vesselsDoc = "" - let othersDoc = "" - - let md = '# Signal K Data Model Reference\n\n' - - md += 'This document is meant as the human-oriented reference to accompany the actual JSON Schema specification and is produced from the schema files. Any changes to the reference material below should be made to the original schema files.\n\n' - - md += "Signal K uses [SI units](https://en.wikipedia.org/wiki/International_System_of_Units) almost everywhere, with the exception of geographic coordinates, which are in degrees in WGS84. The following units are in use:\n" - - _.forOwn(units, (value, key) => { - md += `- ${key} : ${value}\n` - }) - md += "\n## Keys\n" - - Object.keys(filenames).forEach(fn => { - let valid = true - filter.forEach(f => { - if (filenames[fn].indexOf(f) !== -1) { - valid = false - } - }) - if (!valid) { - return - } - - md = fn.split('.')[0] === 'vessels' ? vesselsDoc : othersDoc - const path = filenames[fn] - const doc = this.docs[path] - - function isEmbedded(path) { - if (this.docs[`${path}/timestamp`]) { - return false - } - const parts = path.split('/').filter(x => x.length > 0) - let soFar = "" - let result = false - parts.forEach(part => { - soFar = soFar + "/" + part - result = result || !!this.docs[soFar + '/timestamp'] - }) - return result - } - - let json = doc.json - - const key = path.replace(//g, '').replace('RegExp', '*') - keysWithMeta[key] = { - units: json.units, - description: doc.description === null ? '[missing]' : doc.description - } - if (doc.enum) { - keysWithMeta[key].enum = doc.enum - } - - if (isEmbedded.bind(this)(path)) { - this.debug("Skipping embedded", path) - return - } - - // md += `### [${path.replace(//g, '>')}](http://signalk.org/specification/master/keys/html/${fn.replace('.md', '.html')})\n\n` - md += `#### ${path.replace(//g, '>')}\n\n` - - if (doc.subtitle !== null) { - md += `**Title:** ${doc.subtitle}\n\n` - } - - if (json !== null && typeof json.units === 'string') { - md += `**Units:** ${json.units} (${units[json.units]})\n\n` - } - - md += '**Description:** ' - md += (doc.description === null ? '[missing]' : doc.description) - md += '\n\n' - - if (doc.enum) { - md += '**Enum values:**\n\n' - doc.enum.forEach(enumValue => md += `* ${enumValue}\n`) - md += '\n' - } - - if (json.properties && json.properties['value'] && json.properties['value'].type === 'object') { - md += 'Object value with properties\n' - keysWithMeta[key].properties = JSON.parse(JSON.stringify(json.properties['value'].properties)) - Object.keys(json.properties['value'].properties).forEach(propName => { - md += `* ${propName}` - if (json.properties['value'].properties[propName].units) { - md += ` (${json.properties['value'].properties[propName].units})` - } - md += '\n' - }) - md += '\n' - } - - function renderField(key, field, baseIndent) { - const descString = field.description ? ` (${field.description})` : '' - const unitString = field.units ? `, units: ${field.units} (${units[field.units]})` : '' - const enumString = field.enum ? `, enum:\n\n${field.enum.map(x => `${baseIndent} * ${x}\n`).join('')}` : '' - md += `${baseIndent}* ${key}${descString}${unitString}${enumString}\n` - if (field.properties) { - md += '\n' - const subKeys = Object.keys(field.properties) - subKeys.forEach(subKey => renderField(subKey, field.properties[subKey], baseIndent + " ")) - } - } - - if (doc.embeddedFields) { - md += '**Fields:**\n\n' - Object.keys(doc.embeddedFields).forEach(key => { - const field = doc.embeddedFields[key] - renderField(key, field, "") - }) - md += '\n' - } - - md += '---\n\n' - - if (fn.split('.')[0] === 'vessels') { - vesselsDoc = md - } else { - othersDoc = md - } - }) - - fs.writeFileSync(path.join(__dirname, '../src/keyswithmetadata.json'), JSON.stringify(keysWithMeta, null, 2)) - fs.writeFileSync(path.join(__dirname, '../gitbook-docs/vesselsBranch.md'), vesselsDoc) - fs.writeFileSync(path.join(__dirname, '../gitbook-docs/otherBranches.md'), othersDoc) - }) - - /* - * Parse all .md files to HTML in the {OUTPUT}/html folder (@HACK for gitbook) - */ -/* - .then(results => { - return Promise - .all(results.map(item => { - return this.renderMarkdownFile(item.name) - })) - .then(() => { - return results.map(item => { - item.html = `html/${item.name.replace('.md', '.html')}` - return item - }) - }) - }) -*/ - /* - * Print a report to stdout and exit the program. - */ - .then(results => { - if (typeof this.options.done === 'function') { - this.options.done(results) - } - - process.exit(0) - }) - .catch(err => { - console.error(err.message) - console.error(err.stack) - process.exit(1) - }) - } - - renderMarkdownFile (fn) { - return fs - .readFile(path.join(this.options.output, fn), 'utf-8') - .then(md => { - return markdown.render(md) - }) - .then(html => { - return fs.writeFile(path.join(this.options.output, 'html', fn.replace('.md', '.html')), html, 'utf-8') - }) - } - - generateMarkdown (doc) { - let md = '' - - md += `## ${doc.path_normalised}\n\n` - - if (doc.subtitle !== null) { - md += `*${doc.subitle}*\n` - } - - if (doc.description !== null) { - md += `${doc.description}\n` - } - - if (doc.subtitle !== null || doc.description !== null) { - md += '\n' - } - - md += `* Type: \`${(typeof doc.type === 'string' ? doc.type : JSON.stringify(doc.type))}\`\n` - md += `* Path: \`${doc.path}\`\n` - md += `* Node: \`${doc.node}\`\n\n` - - if (doc.example !== null) { - md += `### Example:\n` - md += `\`\`\`\n` - md += `${doc.example}\n` - md += `\`\`\`\n\n` - } - - md += `### Source:\n` - md += `\`\`\`\n` - md += `${doc.json}\n` - md += `\`\`\`\n\n` - - md += '---\n' - return md - } - - hasProperties (data) { - return (typeof data === 'object' && data !== null && (typeof data.properties !== 'undefined' || typeof data.patternProperties !== 'undefined')) - } - - parseProperties (prefix, data) { - if (prefix.charAt(prefix.length - 1) === '/') { - prefix = prefix.replace(/\/+$/, '') - } - const splitPrefix = prefix.split("/") - if (splitPrefix.length > 1 && splitPrefix[splitPrefix.length - 2] === splitPrefix[splitPrefix.length - 1]) { - delete this.tree[prefix] - this.debug("Avoiding self recursion at", prefix) - return - } - - if (typeof data.properties === 'object' && data.properties !== null) { - Object.keys(data.properties).forEach(key => { - this.tree[`${prefix}/${key}`] = data.properties[key] - - if (typeof this.tree[`${prefix}/${key}`] !== 'undefined' && typeof this.tree[`${prefix}/${key}`].allOf !== 'undefined') { - this.parseAllOf(`${prefix}/${key}`, this.tree[`${prefix}/${key}`].allOf, this.tree[`${prefix}/${key}`] || {}) - } - - if (this.hasProperties(this.tree[`${prefix}/${key}`])) { - this.parseProperties(`${prefix}/${key}`, this.tree[`${prefix}/${key}`]) - } - }) - } - - if (typeof data.patternProperties === 'object' && data.patternProperties !== null) { - Object.keys(data.patternProperties).forEach(key => { - const target = `${prefix}/${key}` - - this.tree[`${prefix}/${key}`] = data.patternProperties[key] - - if (typeof this.tree[`${prefix}/${key}`] !== 'undefined' && typeof this.tree[`${prefix}/${key}`].allOf !== 'undefined') { - this.parseAllOf(`${prefix}/${key}`, this.tree[`${prefix}/${key}`].allOf, - this.tree[`${prefix}/${key}`] || {}) - } - - if (this.hasProperties(this.tree[`${prefix}/${key}`])) { - this.parseProperties(`${prefix}/${key}`, this.tree[`${prefix}/${key}`]) - } - }) - } - - return data - } - - parseAllOf (treePrefix, allOf, baseObject) { - if (!Array.isArray(allOf)) { - return - } - - let readablePrefix = `${treePrefix.split('/')[treePrefix.split('/').length - 2]}/${treePrefix.split('/')[treePrefix.split('/').length - 1]}` - - let temp = this.createAllOfArray(allOf, baseObject) - - this.tree[treePrefix] = this.reduceParsedAllOf(temp) - } - - createAllOfArray (allOf, baseObject) { - if (!Array.isArray(allOf)) { - return {} - } - - const result = [baseObject].concat(allOf) - .filter(obj => { - if (obj === null || typeof obj === 'undefined') { - return false - } - return true - }) - return result - } - - reduceParsedAllOf (allOf, result) { - if (result === null || typeof result !== 'object') { - result = {} - } - - allOf.forEach(obj => { - if (typeof obj !== 'object' || obj === null) { - return - } - - Object.keys(obj).forEach(key => { - if (key !== 'properties' && key !== 'patternProperties' && key !== 'allOf') { - if (!result[key]) { - result[key] = obj[key] - } else if (result[key] !== obj[key]) { - this.debug("avoiding overriding ", key, ". prev", result[key], ", rejected", obj[key]) - } - } - - if (key === 'properties') { - if (typeof result.properties === 'undefined') { - result.properties = {} - } - - Object.keys(obj[key]).forEach(k => { - if (typeof obj[key][k].allOf !== 'undefined') { - this.reduceParsedAllOf(obj[key][k].allOf, obj[key][k]) - } - result.properties[k] = _.merge(result.properties[k] || {}, obj[key][k], (objectValue, sourceValue, key, object, source) => { - if (objectValue && typeof objectValue === "string" && objectValue !== sourceValue) { - this.debug("avoiding overriding ", key, ". prev", objectValue, ", rejected", sourceValue) - return objectValue - } - }) - }) - } - - if (key === 'patternProperties') { - if (typeof result.patternProperties === 'undefined') { - result.patternProperties = {} - } - - Object.keys(obj[key]).forEach(k => { - result.patternProperties[k] = obj[key][k] - }) - } - - if (key === 'allOf') { - this.reduceParsedAllOf(obj[key], result) - } - }) - }) - - return result - } - - parseOptions () { - this.options.entry = this.options.entry.charAt(0) === '/' ? this.options.entry : path.join(this.options.cwd, this.options.entry) - this.options.output = this.options.output.charAt(0) === '/' ? this.options.output : path.join(this.options.cwd, this.options.output) - this.options._definitions = this.options.definitions - this.options.definitions = this.options.definitions.charAt(0) === '/' ? this.options.definitions : path.join(this.options.cwd, this.options.definitions) - - if (this.options.debug === true) { - this.debug = require('debug')('signalk-documentation-generator') - } - } - - rm (path) { - return new Promise((resolve, reject) => { - rimraf(path, (err) => { - if (err) { - return reject(err) - } - - resolve(path) - }) - }) - } -} - -new Parser({ - definitions: './schemas/definitions.json', +new skApi.Parser({ entry: './schemas/signalk.json', - output: './gitbook-docs/keys' -}) + done: function(result) { + fs.writeFileSync(path.join(__dirname, '../gitbook-docs/vesselsBranch.md'), result.vesselsDoc) + fs.writeFileSync(path.join(__dirname, '../gitbook-docs/otherBranches.md'), result.othersDoc) + } +}); \ No newline at end of file diff --git a/src/delta.js b/src/delta.js deleted file mode 100644 index b5bc3b0ad..000000000 --- a/src/delta.js +++ /dev/null @@ -1,66 +0,0 @@ -var _ = require("lodash") - -/* - * Credit for these function goes to @tkurki - */ - -var addToTree = function (pathValue, source, tree) { - var result = {}; - var temp = tree; - var parts = msg.path.split('.'); - for (var i = 0; i < parts.length - 1; i++) { - temp[parts[i]] = {}; - temp = temp[parts[i]]; - } - temp[parts[parts.length - 1]] = msg; - return result; -} - - -function addAsNested(pathValue, source, timestamp, result) { - var temp = result; - var parts = pathValue.path.split('.'); - for (var i = 0; i < parts.length - 1; i++) { - if (typeof temp[parts[i]] === 'undefined') { - temp[parts[i]] = {}; - } - temp = temp[parts[i]]; - }; - - //mapping produced an object like {latitude:...,longitude:...} - if (typeof pathValue.value === 'object') { - temp[parts[parts.length - 1]] = pathValue.value; - temp[parts[parts.length - 1]].source = source; - temp[parts[parts.length - 1]].timestamp = timestamp + ''; - } else { - temp[parts[parts.length - 1]] = { - value: pathValue.value, - source: _.clone(source), - timestamp: timestamp + '' - }; - delete temp[parts[parts.length - 1]].source.timestamp; - } -} - -function valuesToSubTree(delta) { - var valueTree = {}; - var timestamp = delta.updates[0].source.timestamp; - delta.updates[0].values.forEach(function(pathValue) { - addAsNested(pathValue, delta.updates[0].source, timestamp, valueTree); - }); - return valueTree; -} - -function deltaToNested(delta) { - var result = {}; - var contextPointer = result; - var pathPropertyNames = delta.context.split('.'); - for (var i = 0; i < pathPropertyNames.length - 1; i++) { - contextPointer[pathPropertyNames[i]] = {}; - contextPointer = contextPointer[pathPropertyNames[i]]; - }; - contextPointer[pathPropertyNames[pathPropertyNames.length -1]] = valuesToSubTree(delta); - return result; -} - -module.exports.deltaToNested = deltaToNested; diff --git a/src/fullsignalk.js b/src/fullsignalk.js deleted file mode 100644 index b596bc836..000000000 --- a/src/fullsignalk.js +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright 2016, Teppo Kurki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -var _ = require('lodash'); -var signalkSchema = require('./'); -var getId; -var debug = require('debug')('signalk:fullsignalk'); - - -function FullSignalK(id, type, defaults) { - //hack, apparently not available initially, so need to set lazily - getId = signalkSchema.getSourceId; - - this.root = { - vessels: {}, - self: id, - version: "0.1.0" // Should we read this from the package.json file? - }; - if(id) { - this.root.vessels[id] = defaults && defaults.vessels && defaults.vessels.self ? defaults.vessels.self : {}; - this.self = this.root.vessels[id]; - signalkSchema.fillIdentity(this.root) - this.root.self = 'vessels.' + id - } - this.sources = {}; - this.root.sources = this.sources; - this.lastModifieds = {}; -} - -require("util").inherits(FullSignalK, require("events").EventEmitter); - -FullSignalK.prototype.retrieve = function() { - return this.root; -} - -FullSignalK.prototype.addDelta = function(delta) { - this.emit('delta', delta); - var context = findContext(this.root, delta.context); - this.addUpdates(context, delta.context, delta.updates); - this.updateLastModified(delta.context); -}; - -FullSignalK.prototype.updateLastModified = function(contextKey) { - this.lastModifieds[contextKey] = new Date().getTime(); -} - -FullSignalK.prototype.pruneContexts = function(seconds) { - var threshold = new Date().getTime() - seconds * 1000; - for (let contextKey in this.lastModifieds) { - if (this.lastModifieds[contextKey] < threshold) { - this.deleteContext(contextKey); - delete this.lastModifieds[contextKey]; - } - } -} - -FullSignalK.prototype.deleteContext = function(contextKey) { - debug("Deleting context " + contextKey); - var pathParts = contextKey.split('.'); - if(pathParts.length === 2) { - delete this.root[pathParts[0]][pathParts[1]]; - } -} - -function findContext(root, contextPath) { - var context = _.get(root, contextPath); - if(!context) { - context = {}; - _.set(root, contextPath, context); - } - var identity = contextPath.split('.')[1]; - if(!identity) { - return undefined; - } - signalkSchema.fillIdentityField(context, identity); - return context; -} - -FullSignalK.prototype.addUpdates = function(context, contextPath, updates) { - var len = updates.length; - for (var i = 0; i < len; ++i) { - this.addUpdate(context, contextPath, updates[i]); - } -} - -FullSignalK.prototype.addUpdate = function(context, contextPath, update) { - if (typeof update.source != 'undefined') { - this.updateSource(context, update.source, update.timestamp); - } else if(typeof update['$source'] != 'undefined') { - this.updateDollarSource(context, update['$source'], update.timestamp); - } else { - console.error("No source in delta update:" + JSON.stringify(update)); - } - addValues(context, contextPath, update.source || update['$source'], update.timestamp, update.values); -} - -FullSignalK.prototype.updateDollarSource = function(context, dollarSource, timestamp) { - const parts = dollarSource.split('.') - parts.reduce((cursor, part) => { - if(typeof cursor[part] === 'undefined') { - return cursor[part] = {} - } - return cursor[part] - }, this.sources) -} - -FullSignalK.prototype.updateSource = function(context, source, timestamp) { - if(!this.sources[source.label]) { - this.sources[source.label] = {}; - this.sources[source.label].label = source.label; - this.sources[source.label].type = source.type; - } - - if(source.type === 'NMEA2000' || source.src) { - handleNmea2000Source(this.sources[source.label], source, timestamp); - return - } - - if(source.type === 'NMEA0183' || source.sentence) { - handleNmea0183Source(this.sources[source.label], source, timestamp); - return - } - - handleOtherSource(this.sources[source.label], source, timestamp); -} - -function handleNmea2000Source(labelSource, source, timestamp) { - if(!labelSource[source.src]) { - labelSource[source.src] = { - n2k: { - src: source.src, - pgns: {} - } - }; - } - if(source.instance && !labelSource[source.src][source.instance]) { - labelSource[source.src][source.instance] = {} - } - labelSource[source.src].n2k.pgns[source.pgn] = timestamp -} - -function handleNmea0183Source(labelSource, source, timestamp) { - var talker = source.talker || 'II'; - if(!labelSource[talker]) { - labelSource[talker] = { - talker: talker, - sentences: {} - }; - } - labelSource[talker].sentences[source.sentence] = timestamp -} - -function handleOtherSource(sourceLeaf, source, timestamp) { - sourceLeaf.timestamp = timestamp; -} - -function addValues(context, contextPath, source, timestamp, pathValues) { - var len = pathValues.length; - for (var i = 0; i < len; ++i) { - addValue(context, contextPath, source, timestamp, pathValues[i]); - } -} - -function addValue(context, contextPath, source, timestamp, pathValue) { - if (_.isUndefined(pathValue.path) || _.isUndefined(pathValue.value)) { - console.error("Illegal value in delta:" + JSON.stringify(pathValue)); - return; - } - var valueLeaf; - if(pathValue.path.length === 0) { - _.merge(context, pathValue.value) - return - } else { - const splitPath = pathValue.path.split('.'); - valueLeaf = splitPath.reduce(function(previous, pathPart, i) { - if (!previous[pathPart]) { - previous[pathPart] = {}; - let meta = signalkSchema.getMetadata(contextPath + '.' + pathValue.path) - if (meta && i === splitPath.length-1) { - //ignore properties from keyswithmetadata.json - meta = JSON.parse(JSON.stringify(meta)) - delete meta.properties - - previous[pathPart].meta = meta; - } - } - return previous[pathPart]; - }, context); - } - - if(valueLeaf.values) { //multiple values already - var sourceId = getId(source); - if(!valueLeaf.values[sourceId]) { - valueLeaf.values[sourceId] = {}; - } - assignValueToLeaf(pathValue.value, valueLeaf.values[sourceId]); - valueLeaf.values[sourceId].timestamp = timestamp; - setMessage(valueLeaf.values[sourceId], source); - } else if(typeof valueLeaf.value != "undefined" && valueLeaf['$source'] != getId(source)) { - // first multiple value - - var sourceId = valueLeaf['$source']; - var tmp = {}; - copyLeafValueToLeaf(valueLeaf, tmp); - valueLeaf.values = {}; - valueLeaf.values[sourceId] = tmp; - valueLeaf.values[sourceId].timestamp = valueLeaf.timestamp; - - sourceId = getId(source); - valueLeaf.values[sourceId] = {}; - assignValueToLeaf(pathValue.value, valueLeaf.values[sourceId]); - valueLeaf.values[sourceId].timestamp = timestamp; - setMessage(valueLeaf.values[sourceId], source); - } - assignValueToLeaf(pathValue.value, valueLeaf); - if(pathValue.path.length != 0) { - valueLeaf['$source'] = getId(source); - valueLeaf.timestamp = timestamp; - setMessage(valueLeaf, source); - } -} - -function copyLeafValueToLeaf(fromLeaf, toLeaf) { - _.assign(toLeaf, _.omit(fromLeaf, ['$source', 'timestamp', 'meta'])); -} - -function assignValueToLeaf(value, leaf) { - leaf.value = value; -} - -function setMessage(leaf, source) { - if(!source) { - return; - } - if(source.pgn) { - leaf.pgn = source.pgn; - delete leaf.sentence; - } - if(source.sentence) { - leaf.sentence = source.sentence; - delete leaf.pgn; - } -} - - -module.exports = FullSignalK; diff --git a/src/index.js b/src/index.js deleted file mode 100644 index d106843e7..000000000 --- a/src/index.js +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright 2016, Teppo Kurki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -var _ = require('lodash'); -var FullSignalK = require('./fullsignalk'); - - var subSchemas = { - 'notifications': require('../schemas/groups/notifications.json'), - 'communication': require('../schemas/groups/communication.json'), - 'design': require('../schemas/groups/design.json'), - 'navigation': require('../schemas/groups/navigation.json'), - 'electrical': require('../schemas/groups/electrical.json'), - 'environment': require('../schemas/groups/environment.json'), - 'performance': require('../schemas/groups/performance.json'), - 'propulsion': require('../schemas/groups/propulsion.json'), - 'resources': require('../schemas/groups/resources.json'), - 'sails': require('../schemas/groups/sails.json'), - 'sensors': require('../schemas/groups/sensors.json'), - 'sources': require('../schemas/groups/sources.json'), - 'steering': require('../schemas/groups/steering.json'), - 'tanks': require('../schemas/groups/tanks.json') - }; - - -function getTv4() { - var tv4 = require('tv4'); - var vesselSchema = require('../schemas/vessel.json'); - tv4.addSchema('https://signalk.org/specification/1.0.0/schemas/vessel.json', vesselSchema); - var aircraftSchema = require('../schemas/aircraft.json'); - tv4.addSchema('https://signalk.org/specification/1.0.0/schemas/aircraft.json', aircraftSchema); - var atonSchema = require('../schemas/aton.json'); - tv4.addSchema('https://signalk.org/specification/1.0.0/schemas/aton.json', atonSchema); - var sarSchema = require('../schemas/sar.json'); - tv4.addSchema('https://signalk.org/specification/1.0.0/schemas/sar.json', sarSchema); - var definitions = require('../schemas/definitions.json'); - tv4.addSchema('https://signalk.org/specification/1.0.0/schemas/definitions.json', definitions); - - for (var schema in subSchemas) { - tv4.addSchema('https://signalk.org/specification/1.0.0/schemas/groups/' + schema + '.json', subSchemas[schema]); - } - - // HACK! two different IDs should not point to the same schema - var externalGeometry = require('../schemas/external/geojson/geometry.json'); - tv4.addSchema('https://signalk.org/specification/1.0.0/schemas/external/geojson/geometry.json', externalGeometry); - tv4.addSchema('http://json-schema.org/geojson/geometry.json', externalGeometry); - - tv4.addFormat(require('tv4-formats')) - - return tv4; -} - -function validateFull(tree) { - var signalkSchema = require('../schemas/signalk.json'); - - var tv4 = getTv4(); - var valid = getTv4().validateMultiple(tree, signalkSchema, true, true); - var result = tv4.validateResult(tree, signalkSchema, true, true); - //Hack: validateMultiple marks anyOf last match incorrectly as not valid with banUnknownProperties - //https://github.com/geraintluff/tv4/issues/128 - valid.valid = result.valid; - return valid; -} - -function validateDelta(delta, ignoreContext) { - var tv4 = require('tv4'); - var deltaSchema = require('../schemas/delta.json'); - var definitions = require('../schemas/definitions.json'); - tv4.addSchema('https://signalk.org/specification/1.0.0/schemas/definitions.json', definitions); - - if (ignoreContext) { - delta.context = 'ignored the context, so place a placeholder there'; - } - var valid = tv4.validateMultiple(delta, deltaSchema, true, true); - return valid; -} - -function validateWithSchema(msg, schemaName) { - var tv4 = require('tv4'); - var schema = require('../schemas/' + schemaName); - var valid = tv4.validateResult(msg,schema, true, true); - return valid; -} - -function chaiAsPromised(chai, utils) { - "use strict"; - - var Assertion = chai.Assertion - - function checkValidFullSignalK () { - var result = validateFull(this._obj); - - var message = result.errors.reduce(function(msgBuilder, error) { - msgBuilder += error.dataPath + ":" + error.message + "\n"; - return msgBuilder; - }, {}); - this.assert( - result.valid - , message - , 'expected #{this} to not be valid SignalK' - ); - } - Assertion.addProperty('validSignalK', checkValidFullSignalK); - Assertion.addProperty('validFullSignalK', checkValidFullSignalK); - Assertion.addProperty('validSignalKIgnoringSelf', function() { - this._obj.self = 'urn:mrn:imo:mmsi:230099999'; - checkValidFullSignalK.call(this); - }); - Assertion.addProperty('validSignalKVessel', function() { - this._obj = { - 'vessels': { - 'urn:mrn:imo:mmsi:230099999': this._obj - }, - self: 'urn:mrn:imo:mmsi:230099999', - 'version': '1.0.0' - } - checkValidFullSignalK.call(this); - }); - Assertion.addProperty('validSignalKVesselIgnoringIdentity', function() { - this._obj.mmsi = '230099999'; - this._obj = { - 'vessels': { - 'urn:mrn:imo:mmsi:230099999': this._obj - }, - self: 'urn:mrn:imo:mmsi:230099999', - version: "0.0.0" - } - checkValidFullSignalK.call(this); - }); - Assertion.addProperty('validSignalKDelta', function () { - var result = validateDelta(this._obj); - var message = result.errors.length === 0 ? '' : result.errors[0].message + ':' + result.errors[0].dataPath + - ' (' + (result.errors.length-1) + ' other errors not reported here)'; - this.assert( - result.valid - , message - , 'expected #{this} to not be valid SignalK delta' - ); - }); - Assertion.addProperty('validSubscribeMessage', function () { - var result = validateWithSchema(this._obj, 'messages/subscribe.json'); - var message = result.error ? result.error.message + ':' + result.error.dataPath : ''; - this.assert( - result.valid - , message - , 'expected #{this} to not be valid SignalK subscribe message' - ); - }); - Assertion.addProperty('validUnsubscribeMessage', function () { - var result = validateWithSchema(this._obj, 'messages/unsubscribe.json'); - var message = result.error ? result.error.message + ':' + result.error.dataPath : ''; - this.assert( - result.valid - , message - , 'expected #{this} to not be valid SignalK unsubscribe message' - ); - }); - Assertion.addProperty('validDiscovery', function () { - var result = validateWithSchema(this._obj, 'discovery'); - var message = result.error ? result.error.message + ':' + result.error.dataPath : ''; - this.assert( - result.valid - , message - , 'expected #{this} to not be valid SignalK discovery document' - ); - }); -} - - -//FIXME does not account for multiple sources for a single path in a single delta -module.exports.deltaToFullVessel = function(delta) { - var result = {}; - if (delta.updates) { - delta.updates.forEach(function(update) { - if (update.values) { - update.values.forEach(function(pathValue) { - if (typeof pathValue.value === 'object') { - _.set(result, pathValue.path, pathValue.value); - } else { - _.set(result, pathValue.path + '.value', pathValue.value); - } - _.set(result, pathValue.path + '.timestamp', update.timestamp); - if (update.source) { - if (update.source.pgn) { - _.set(result, pathValue.path + '.pgn', update.source.pgn); - } - if (!_.isUndefined(update.source.label) && update.source.src) { - _.set(result, pathValue.path + "['$source']", update.source.label + '.' + update.source.src); - } - } - _.set(result, pathValue.path + '.timestamp', update.timestamp); - }) - } - }) - } - return result; -} - -module.exports.deltaToFull = function(delta) { - var fullSignalK = new FullSignalK(); - fullSignalK.addDelta(delta); - var result = fullSignalK.retrieve(); - fillIdentity(result); - return result; -} - -function fillIdentity(full) { - let identity - for (identity in full.vessels) { - fillIdentityField(full.vessels[identity], identity); - //fill arbitrarily the last id as self, used in tests - full.self = identity - } -} - -var mmsiPrefixLenght = 'urn:mrn:imo:mmsi:'.length; -function fillIdentityField(vesselData, identity) { - if (identity.indexOf('urn:mrn:imo') === 0) { - vesselData.mmsi = identity.substring(mmsiPrefixLenght, identity.length) - } else if (identity.indexOf('urn:mrn:signalk') === 0) { - vesselData.uuid = identity - } else { - vesselData.url = identity; - } -} - -function getSourceId(source) { - if (!source) { - return 'no_source'; - } - if (source.src || source.pgn) { - return source.label + - (source.src ? '.' + source.src : '') + - (source.instance ? '.' + source.instance : ''); - } - if (typeof source === 'object') { - return source.label + (source.talker ? '.' + source.talker : '.XX'); - } - //source data is actually from $source, not source: {...} - return source -} - -function keyForSourceIdPath(sourceId, path) { - return sourceId + "." + path; -} - -module.exports.fillIdentityField = fillIdentityField; - -module.exports.validateFull = validateFull; -module.exports.validateVessel = function(vesselData) { - return validateFull({ - 'vessels': { - 'urn:mrn:imo:mmsi:230099999': vesselData - } - }); -} -module.exports.fillIdentity = fillIdentity; -module.exports.validateDelta = validateDelta; -module.exports.chaiModule = chaiAsPromised; -module.exports.getTv4 = getTv4; -module.exports.subSchemas = subSchemas; -module.exports.units = require('../schemas/definitions').definitions.units; -module.exports.metadata = require('./keyswithmetadata'); -module.exports.FullSignalK = FullSignalK; -module.exports.fakeMmsiId = "urn:mrn:imo:mmsi:230099999"; -module.exports.getSourceId = getSourceId; -module.exports.keyForSourceIdPath = keyForSourceIdPath; - -module.exports.metadata = require('./keyswithmetadata'); - -var metadataByRegex = [] -_.forIn(module.exports.metadata, (value, key) => { - const regexpKey = - '^' + key.replace(/\*/g, '.*').replace(/RegExp/g, '.*') + '$' - if (!regexpKey.endsWith('.*$')) { - metadataByRegex.push({ - regexp: new RegExp(regexpKey), - metadata: value - }) - } -}) - -module.exports.getUnits = function (path) { - const meta = module.exports.getMetadata(path) - return meta ? meta.units : undefined -} - -module.exports.getMetadata = function (path) { - const result = metadataByRegex.find(entry => - entry.regexp.test('/' + path.replace(/\./g, '/')) - ) - return result ? result.metadata : undefined -} - -module.exports.getAISShipTypeName = function(id) { - const the_enum = subSchemas['design'].properties.aisShipType.allOf[1].properties.value.allOf[1].enum; - //const the_enum = module.exports.getMetadata('vessels.foo.design.aisShipType').enum - var res = the_enum.find(item => { return item.id == id }); - return res ? res.name : undefined -} - - -module.exports.getAtonTypeName = function(id) { - const the_enum = require('../schemas/aton.json').properties.atonType.allOf[1].properties.value.allOf[1].enum; - var res = the_enum.find(item => { return item.id == id }); - return res ? res.name : undefined -} diff --git a/test/fullsignalk.js b/test/fullsignalk.js deleted file mode 100644 index 389f7f03d..000000000 --- a/test/fullsignalk.js +++ /dev/null @@ -1,191 +0,0 @@ -var chai = require('chai'); -chai.Should(); -chai.use(require('../dist/').chaiModule); - -var FullSignalK = require('../src/fullsignalk'); - - - -describe('FullSignalK', function() { - it('Delta with object value should produce full tree leaf without the .value', function() { - var delta = { - "updates": [{ - "source": { - "label": "n2kFromFile", - "type": "NMEA2000", - "pgn": 129038, - "src": "43" - }, - "timestamp": "2014-08-15T19:03:21.532Z", - "values": [{ - "path": "navigation.speedOverGround", - "value": 7.09 - }, { - "path": "navigation.courseOverGroundTrue", - "value": 4.8171 - }, { - "path": "navigation.position", - "value": { - "longitude": 25.4398883, - "latitude": 59.969895 - } - }] - }], - "context": "vessels.foo" - }; - var fullSignalK = new FullSignalK(); - fullSignalK.addDelta(delta); - fullSignalK.retrieve().vessels.foo.navigation.position.value.should.have.property('longitude'); - fullSignalK.retrieve().vessels.foo.navigation.position.should.have.property('$source'); - }) - - it('Two deltas from different sources results in values structure', function() { - var delta = { - "updates": [{ - "source": { - "label": "n2kFromFile", - "type": "NMEA2000", - "pgn": 129038, - "src": "43" - }, - "timestamp": "2014-08-15T19:03:21.532Z", - "values": [{ - "path": "navigation.speedOverGround", - "value": 7.09 - }] - }], - "context": "vessels.foo" - }; - var fullSignalK = new FullSignalK(); - fullSignalK.addDelta(delta); - delta.updates[0].source.src = 48; - delta.updates[0].values[0].value = 8; - fullSignalK.addDelta(delta); - fullSignalK.retrieve().vessels.foo.navigation.speedOverGround.should.have.property('value', 8); - fullSignalK.retrieve().vessels.foo.navigation.speedOverGround.should.have.property('$source'); - fullSignalK.retrieve().vessels.foo.navigation.speedOverGround.values['n2kFromFile.43'].should.have.property('value', 7.09); - fullSignalK.retrieve().vessels.foo.navigation.speedOverGround.values['n2kFromFile.48'].should.have.property('value', 8); - }) - - it('AIS delta produces valid Signal K', function() { - var aisDelta = { - "updates": [{ - "source": { - "label": "N2K-1", - "type": "NMEA2000", - "pgn": 129038, - "src": "43" - }, - "timestamp": "2014-08-15T19:00:15.402Z", - "values": [{ - "path": "navigation.speedOverGround", - "value": 14.81 - }, { - "path": "navigation.courseOverGroundTrue", - "value": 3.4889 - }, { - "path": "navigation.position", - "value": { - "longitude": 24.8142433, - "latitude": 59.865655 - } - }] - }], - "context": "vessels.urn:mrn:imo:mmsi:276780000" - }; - var fullSignalK = new FullSignalK("urn:mrn:imo:mmsi:276799999", "mmsi"); - fullSignalK.addDelta(aisDelta); - fullSignalK.retrieve().should.be.validSignalK; - - }) - - it('Delta with empty path sets content under root', function() { - var msg = { - "updates": [{ - "source": { - "label": "n2kFromFile", - "type": "NMEA2000", - "pgn": 129794, - "src": "43" - }, - "timestamp": "2014-08-15T19:02:31.507Z", - "values": [{ - "path": "", - "value": { - "name": "WRANGO" - } - }] - }], - "context": "vessels.urn:mrn:imo:mmsi:276810000" - } - var fullSignalK = new FullSignalK(); - fullSignalK.addDelta(msg); - var vessel = fullSignalK.retrieve().vessels['urn:mrn:imo:mmsi:276810000']; - vessel.should.have.property('name', "WRANGO"); - vessel.should.not.have.property('$source'); - vessel.should.not.have.property('timestamp'); - vessel.should.not.have.property('pgn'); - }) - - it('Delta with instance produces proper sources hierarchy', function() { - - var msg = { - "updates": [{ - "source": { - "label": "N2K", - "type": "NMEA2000", - "pgn": 130312, - "src": "36", - "instance": "0" - }, - "timestamp": "2015-01-15T16:15:19.628Z", - "values": [{ - "path": "environment.water.temperature", - "value": 15.2 - }] - }], - "context": "vessels.urn:mrn:imo:mmsi:276810000" - } - var fullSignalK = new FullSignalK(); - fullSignalK.addDelta(msg); - var full = fullSignalK.retrieve(); - var vessel = full.vessels['urn:mrn:imo:mmsi:276810000']; - vessel.environment.water.temperature.should.have.property('value', 15.2); - full.sources.should.have.property('N2K'); - full.sources['N2K'].should.have.property('36'); - full.sources['N2K']['36'].should.have.property('0'); - }) - - it('Delta with $source produces sources hierarchy and correct $source reference', function() { - - var msg = { - "context": "vessels.urn:mrn:imo:mmsi:276810000", - "updates": [{ - "$source": "1W.0316013faeff", - "values": [{ - "path": "propulsion.engine1.temperature", - "value": 301.837 - }] - }] - } - - var fullSignalK = new FullSignalK(); - fullSignalK.addDelta(msg); - var full = fullSignalK.retrieve(); - full.sources.should.have.property('1W'); - full.sources['1W'].should.have.property('0316013faeff'); - var vessel = full.vessels['urn:mrn:imo:mmsi:276810000']; - vessel.propulsion.engine1.temperature.should.have.property('$source', '1W.0316013faeff') - }) - - it('MMSI self is set correctly in full tree', function() { - var fullSignalK = new FullSignalK('urn:mrn:imo:mmsi:276810000', null, {}); - fullSignalK.retrieve().self.should.equal('vessels.urn:mrn:imo:mmsi:276810000') - }) - - it('UUID self is set correctly in full tree', function() { - var fullSignalK = new FullSignalK('urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d', null, {}); - fullSignalK.retrieve().self.should.equal('vessels.urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d') - }) - -}) diff --git a/test/jsonSamplesAndTestFiles.js b/test/jsonSamplesAndTestFiles.js index 6782d456c..3a1babca7 100644 --- a/test/jsonSamplesAndTestFiles.js +++ b/test/jsonSamplesAndTestFiles.js @@ -1,6 +1,6 @@ var assert = require('assert'); var chai = require('chai'); chai.Should(); -chai.use(require('../dist/').chaiModule); +chai.use(require('@bkp7/schema-js-api').chaiModule); describe('Samples', function() { diff --git a/test/schema-api.js b/test/schema-api.js deleted file mode 100644 index 1897c1e97..000000000 --- a/test/schema-api.js +++ /dev/null @@ -1,73 +0,0 @@ -var chai = require('chai') -chai.Should() -const expect = chai.expect -const signalkSchema = require('../dist/') - -describe('metadata:getUnits', function () { - it('Valid simple getUnits works', function () { - signalkSchema.getUnits('vessels.foo.navigation.speedOverGround').should.equal('m/s') - }) - - it('Valid complex path getUnits works', function () { - signalkSchema.getUnits('vessels.foo.propulsion.0.oilTemperature').should.equal('K') - }) - - it('Invalid getUnits returns undefined', function () { - expect(signalkSchema.getUnits('vessels.foo.bar.0.oilTemperature')).to.be.undefined - }) -}) - -describe('metadata:getMetadata', function () { - it('Valid simple getMetadata works', function () { - signalkSchema.getMetadata('vessels.foo.navigation.speedOverGround').should.deep.equal({ - units: 'm/s', - description: "Vessel speed over ground. If converting from AIS 'HIGH' value, set to 102.2 (Ais max value) and add warning in notifications" - }) - }) - - it('Valid complex path getMetadata works', function () { - signalkSchema.getMetadata('vessels.foo.propulsion.0.oilTemperature').should.deep.equal({ - units: 'K', - description: 'Oil temperature' - }) - }) - - it('Invalid getMetadata returns undefined', function () { - expect(signalkSchema.getMetadata('vessels.foo.0.oilTemperature')).to.be.undefined - }) - - it('getMetadata for path with enum works', function () { - expect( - signalkSchema.getMetadata('vessels.foo.navigation.datetime.gnssTimeSource') - ).to.deep.equal({ - description: 'Source of GNSS Date and Time', - enum: [ - 'GPS', - 'GLONASS', - 'Galileo', - 'Beidou', - 'IRNSS', - 'Radio Signal', - 'Internet', - 'Local clock' - ] - }) - }) -}) - -describe('getAISShipTypeName works', function() { - it("ship type 20 is 'Wing In Ground'", function() { - expect( - signalkSchema.getAISShipTypeName(20) - ).to.deep.equal('Wing In Ground') - }); -}); - - -describe('getAtonTypeName works', function() { - it("ship type 11 is 'Beacon, Cardinal S'", function() { - expect( - signalkSchema.getAtonTypeName(11) - ).to.deep.equal('Beacon, Cardinal S') - }); -}); diff --git a/test/schemaReferences.js b/test/schemaReferences.js index ca6d36cd6..acb95fdc9 100644 --- a/test/schemaReferences.js +++ b/test/schemaReferences.js @@ -1,25 +1,12 @@ var assert = require('chai').assert -var signalk = require('../'); +var signalk = require('@bkp7/schema-js-api'); var RefParser = require('json-schema-ref-parser'); var path = require('path'); -describe('Schema references', function() { - it('missing files are not referenced', function() { - var signalkSchema = require('../schemas/signalk.json'); - var tv4 = signalk.getTv4(); - - tv4.validate({}, signalkSchema); - assert.lengthOf(tv4.getMissingUris(), 0, 'There should be no missing schema uris, but found ' + tv4.getMissingUris()); +describe('Schema integrity', function() { + it('schemas loaded without errors', function() { + assert(signalk.schemas.status === 'loaded'); }) - it('all references are valid', function(done) { - RefParser.dereference(path.join(__dirname, '../schemas/signalk.json'), function(err, schema) { - if (err) { - done(err); - } else { - done(); - } - }); - }) }); diff --git a/test/sources.js b/test/sources.js deleted file mode 100644 index 5640a3a53..000000000 --- a/test/sources.js +++ /dev/null @@ -1,109 +0,0 @@ -const chai = require('chai'); -const should = chai.should() -chai.use(require('../dist/').chaiModule); -const FullSignalK = require('../src/fullsignalk') -const debug = require('debug')('test:sources') - -var deltaWithMiscSources = { - "context": "vessels.urn:mrn:imo:mmsi:200000000", - "updates": [{ - "source": { - "sentence": "HDT", - "label": "0183-1", - "talker": "II" - }, - "timestamp": "2016-08-03T07:55:57.000Z", - "values": [{ - "path": "navigation.headingTrue", - "value": 0.2231 - }] - }, { - "source": { - "src": "37", - "pgn": 127251, - "label": "N2000-01" - }, - "timestamp": "2016-06-20T10:33:36Z", - "values": [{ - "path": "navigation.rateOfTurn", - "value": 0.108908 - }] - }, { - "$source": "1W.0316013faeff", - "timestamp": "2016-07-28T18:18:46.074Z", - "values": [{ - "path": "propulsion.engine1.temperature", - "value": 301.837 - }] - }, { - "$source": "i2c-0.0x48.volts", - "timestamp": "2016-07-28T18:18:46.074Z", - "values": [{ - "path": "electrical.batteries.house.voltage", - "value": 12.837 - }] - }, { - "$source": "i2c-0.0x48.amps", - "timestamp": "2016-07-28T18:18:46.074Z", - "values": [{ - "path": "electrical.batteries.house.current", - "value": -0.837 - }] - }, { - "timestamp": "2016-08-03T07:55:57.000Z", - "values": [{ - "path": "navigation.headingTrue", - "value": 0.2231 - }] - }] -} - -describe('Sources in delta', function() { - it("are valid", function() { - var fullSignalK = new FullSignalK('urn:mrn:imo:mmsi:200000000') - fullSignalK.addDelta(deltaWithMiscSources) - var full = fullSignalK.retrieve() - full.sources['0183-1']['II'].talker.should.equal('II') - full.sources['N2000-01']['37']['n2k']['src'].should.equal('37') - should.exist(full.sources['i2c-0']['0x48']) - should.exist(full.sources['1W']['0316013faeff']) - //FIXME for some reason tv4 complains about source's type property being undefined - // renaming the type property of the source fixes the problem - // fix with a better validation tool or dig deeper - // full.should.be.validSignalK - deltaWithMiscSources.should.be.validSignalKDelta; - }); -}); - -describe('Delta with source.instance', function() { - it("produces valid full", function() { - const delta = { - "context": "vessels.urn:mrn:imo:mmsi:200000000", - "updates": [ - { - "source": { - "label": "aLabel", - "type": "NMEA2000", - "pgn": 130312, - "src": "41", - "instance": "5" - }, - "timestamp": "2015-01-15T16:15:18.136Z", - "values": [ - { - "path": "environment.inside.engineRoom.temperature", - "value": 70 - } - ] - } - ] - } - delta.should.be.validSignalKDelta - - const fullSignalK = new FullSignalK('urn:mrn:imo:mmsi:200000000'); - fullSignalK.addDelta(delta); - const full = fullSignalK.retrieve(); - debug((JSON.stringify(full, null, 2))) - full.should.be.validSignalK - }) -});