From 3d4190ebcc164fb0c19fc2ef57d5fcdac72a3f83 Mon Sep 17 00:00:00 2001 From: xufengli Date: Thu, 25 Jan 2018 17:45:49 +0000 Subject: [PATCH 1/2] set up test framework for client, server and the travis Signed-off-by: xufengli --- .travis.yml | 2 +- .travis/before_install.sh | 11 +++++ client/.vscode/launch.json | 18 +++++++ client/package.json | 21 ++++---- client/test/data/permissions.acl | 0 client/test/data/test.cto | 41 ++++++++++++++++ client/test/extension.test.ts | 33 +++++++++++-- client/tsconfig.json | 3 +- server/package.json | 82 +++++++++++++++++++++----------- server/src/server.ts | 3 +- server/test/server.spec.ts | 27 +++++++++++ 11 files changed, 197 insertions(+), 44 deletions(-) create mode 100755 .travis/before_install.sh create mode 100644 client/test/data/permissions.acl create mode 100644 client/test/data/test.cto create mode 100644 server/test/server.spec.ts diff --git a/.travis.yml b/.travis.yml index b620794..b4eddd2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: node_js node_js: - - '6' + - '8' dist: trusty install: | ./.travis/install.sh diff --git a/.travis/before_install.sh b/.travis/before_install.sh new file mode 100755 index 0000000..a1ce32a --- /dev/null +++ b/.travis/before_install.sh @@ -0,0 +1,11 @@ +#!/bin/bash +#-- script to automate preinstall, server compile, and package +# Exit on first error, print all commands. +set -ev +set -o pipefail + +if [$TRAVIS_OS_NAME == 'linux']; then + export CXX="g++-4.9" CC="gcc-4.9" DISPLAY=:99.0; + sh -e /etc/init.d/xvfb start; + sleep 3; +fi \ No newline at end of file diff --git a/client/.vscode/launch.json b/client/.vscode/launch.json index 8308223..52726c2 100755 --- a/client/.vscode/launch.json +++ b/client/.vscode/launch.json @@ -2,6 +2,24 @@ { "version": "0.2.0", "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Mocha Tests", + "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", + "args": [ + "-u", + "tdd", + "--no-timeouts", + "--compilers", + "ts:ts-node/register", + "--colors", + "${workspaceFolder}/test/**/*.test.ts" + ], + "cwd": "${workspaceFolder}", + "protocol": "inspector", + "internalConsoleOptions": "openOnSessionStart" + }, { "name": "Launch Client Extension", "type": "extensionHost", diff --git a/client/package.json b/client/package.json index c6cd756..caaddd3 100755 --- a/client/package.json +++ b/client/package.json @@ -15,7 +15,7 @@ "publisher": "HyperledgerComposer", "icon": "icon.png", "engines": { - "vscode": "^1.15.1" + "vscode": "^1.19.2" }, "repository": { "type": "git", @@ -248,21 +248,26 @@ }, "scripts": { "compile:client": "tsc -p ./", - "watch:client": "tsc -w -p ./", + "watch:client": "tsc -w -p ./", "update-vscode": "node ./node_modules/vscode/bin/install", "postinstall": "node ./node_modules/vscode/bin/install", "package:vsix": "node ./node_modules/vsce/out/vsce package", "prepublish": "tsc -p ./", - "test": "" + "test": "node ./node_modules/vscode/bin/test" }, "devDependencies": { + "@types/chai": "^3.5.2", "@types/mocha": "^2.2.33", "@types/node": "^6.0.52", - "typescript": "^2.1.5", - "vscode": "^1.1.5", - "vsce": "^1.30.0" + "chai": "^4.1.2", + "mocha": "^5.0.0", + "nyc": "^11.4.1", + "ts-node": "^4.1.0", + "typescript": "^2.6.2", + "vsce": "^1.30.0", + "vscode": "^1.1.5" }, "dependencies": { - "vscode-languageclient": "^3.3.0" + "vscode-languageclient": "^3.5.0" } -} \ No newline at end of file +} diff --git a/client/test/data/permissions.acl b/client/test/data/permissions.acl new file mode 100644 index 0000000..e69de29 diff --git a/client/test/data/test.cto b/client/test/data/test.cto new file mode 100644 index 0000000..572ed57 --- /dev/null +++ b/client/test/data/test.cto @@ -0,0 +1,41 @@ +/* + * 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. + */ + +namespace net.biz.digitalPropertyNetwork + +asset LandTitle identified by titleId { + o String titleId + --> Person owner + o String information + o Boolean forSale optional +} + +asset SalesAgreement identified by salesId { + o String salesId + --> Person buyer + --> Person seller + --> LandTitle title +} + +participant Person identified by personId { + o String personId + o String firstName + o String lastName +} + +transaction RegisterPropertyForSale{ + o String transactionIdd + --> Person seller + --> LandTitle title +} \ No newline at end of file diff --git a/client/test/extension.test.ts b/client/test/extension.test.ts index 1cec6f4..a73c889 100755 --- a/client/test/extension.test.ts +++ b/client/test/extension.test.ts @@ -9,14 +9,39 @@ import * as assert from 'assert'; // You can import and use all API from the 'vscode' module // as well as import your extension to test it import * as vscode from 'vscode'; +import * as path from 'path'; import * as myExtension from '../src/extension'; +import { ExtensionContext, workspace, Uri } from 'vscode'; +import { error } from 'util'; // Defines a Mocha test suite to group tests of similar kind together suite("Extension Tests", () => { - // Defines a Mocha unit test - test("Something 1", () => { - assert.equal(-1, [1, 2, 3].indexOf(5)); - assert.equal(-1, [1, 2, 3].indexOf(0)); + + // test validation cto + test("activate", () => { + + // let context: ExtensionContext; + // let serverModule = context.asAbsolutePath(path.join('../server', 'server.js')); + + let uri = vscode.Uri.file(path.join("/Users/Fenglian/dev/git/composer-vscode-plugin/client/test/data", 'test.cto')); + + // let uri = vscode.Uri.file(path.join(vscode.workspace.rootPath || '', './data/test.cto')); + console.log('uri' + uri); + workspace.openTextDocument(uri).then((document) =>{ + + let text = document.getText(); + console.log('text = ' + text); + + }, (err) =>{ + assert.ok(false, `error in OpenTextDocument ${err}`); + return Promise.reject(err); + }); + + // let serverOptions: ServerOptions = { + // run: { module: serverModule, transport: TransportKind.ipc } + // } + console.log('is activate called?'); + }); }); \ No newline at end of file diff --git a/client/tsconfig.json b/client/tsconfig.json index aafa13d..79f9b25 100755 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -14,6 +14,7 @@ }, "exclude": [ "node_modules", - "server" + "server", + ".vscode-test" ] } \ No newline at end of file diff --git a/server/package.json b/server/package.json index a481efb..ce04788 100755 --- a/server/package.json +++ b/server/package.json @@ -1,29 +1,53 @@ -{ - "name": "composer-support-server", - "description": "HyperledgerComposer server", - "version": "0.16.2", - "author": "Hyperledger Composer", - "publisher": "HyperledgerComposer", - "license": "Apache-2.0", - "engines": { - "node": "*" - }, - "repository": { - "type": "git", - "url": "https://github.com/hyperledger/composer-vscode-plugin" - }, - "dependencies": { - "vscode-languageserver": "3.3.0", - "vscode-uri": "1.0.1", - "composer-common": "latest" - }, - "devDependencies": { - "@types/node": "^6.0.52", - "typescript": "^2.1.5" - }, - "scripts": { - "install:server": "installServerIntoExtension ../client ./package.json ./tsconfig.json", - "compile:server": "installServerIntoExtension ../client ./package.json ./tsconfig.json && tsc -p .", - "watch:server": "installServerIntoExtension ../client ./package.json ./tsconfig.json && tsc -w -p ." - } -} +{ + "name": "composer-support-server", + "description": "HyperledgerComposer server", + "version": "0.16.2", + "author": "Hyperledger Composer", + "publisher": "HyperledgerComposer", + "license": "Apache-2.0", + "engines": { + "node": "*" + }, + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/composer-vscode-plugin" + }, + "dependencies": { + "composer-common": "latest", + "vscode-languageserver": "3.3.0", + "vscode-uri": "1.0.1" + }, + "devDependencies": { + "@types/mocha": "^2.2.33", + "@types/node": "^6.0.52", + "chai": "^4.1.2", + "mocha": "^5.0.0", + "nyc": "^11.4.1", + "ts-node": "^4.1.0", + "typescript": "^2.6.2" + }, + "scripts": { + "install:server": "installServerIntoExtension ../client ./package.json ./tsconfig.json", + "compile:server": "installServerIntoExtension ../client ./package.json ./tsconfig.json && tsc -p .", + "watch:server": "installServerIntoExtension ../client ./package.json ./tsconfig.json && tsc -w -p .", + "test": "nyc ./node_modules/.bin/mocha --compilers ts:ts-node/register ./test/*.spec.ts" + }, + "nyc": { + "exclude": [ + "coverage/**", + "out/**", + "scripts/**", + "test/**" + ], + "reporter": [ + "text-summary", + "html" + ], + "all": true, + "check-coverage": true, + "statements": 100, + "branches": 100, + "functions": 100, + "lines": 100 + } +} diff --git a/server/src/server.ts b/server/src/server.ts index bc686dd..6577774 100755 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -668,4 +668,5 @@ connection.onDidCloseTextDocument((params) => { */ // Listen on the connection -connection.listen(); \ No newline at end of file +connection.listen(); +// handleGenerateUml; \ No newline at end of file diff --git a/server/test/server.spec.ts b/server/test/server.spec.ts new file mode 100644 index 0000000..468e64b --- /dev/null +++ b/server/test/server.spec.ts @@ -0,0 +1,27 @@ +// +// Note: This example test is leveraging the Mocha test framework. +// Please refer to their documentation on https://mochajs.org/ for help. +// + +// The module 'assert' provides assertion methods from node +import * as assert from 'assert'; + +// You can import and use all API from the 'vscode' module +// as well as import your extension to test it +import * as path from 'path'; +import { error } from 'util'; +import * as server from '../src/server'; + +// Defines a Mocha test suite to group tests of similar kind together +describe("Server Tests", () => { + + + // test validation cto + it("validateCtoModelFile", () => { + + let originatingFileName: string = "./test/data/test.cto"; + console.log('is server called?'); + // server.validateCtoModelFile( originatingFileName); + + }); +}); \ No newline at end of file From 9ff676ede406aa369ce168830c6cd1f7afe947c1 Mon Sep 17 00:00:00 2001 From: xufengli Date: Thu, 25 Jan 2018 19:50:21 +0000 Subject: [PATCH 2/2] Change the code coverage report to be 0 for at moment. Update the client json script Add Apach 2 copyright to the test files Add one invalid test file Add tslint to check all *.ts format. update minor changes. remove the failued test case. Signed-off-by: xufengli --- .travis.yml | 14 +- .travis/before_install.sh | 11 - .travis/script.sh | 3 +- client/.vscodeignore | 1 + client/package.json | 32 +- client/src/extension.ts | 223 ++++---- .../{permissions.acl => invalid/invalid.txt} | 0 client/test/data/valid/acl/permissions.acl | 34 ++ client/test/data/valid/cto/bond.cto | 93 ++++ client/test/data/{ => valid/cto}/test.cto | 0 client/test/data/valid/qry/queries.qry | 106 ++++ client/test/extension.test.ts | 66 +-- client/tsconfig.json | 8 +- client/tslint.json | 61 +++ server/License.txt | 18 +- server/package.json | 26 +- server/src/server.ts | 477 +++++++++--------- server/test/server.spec.ts | 41 +- server/tslint.json | 70 +++ 19 files changed, 854 insertions(+), 430 deletions(-) delete mode 100755 .travis/before_install.sh rename client/test/data/{permissions.acl => invalid/invalid.txt} (100%) create mode 100644 client/test/data/valid/acl/permissions.acl create mode 100644 client/test/data/valid/cto/bond.cto rename client/test/data/{ => valid/cto}/test.cto (100%) create mode 100644 client/test/data/valid/qry/queries.qry create mode 100644 client/tslint.json create mode 100644 server/tslint.json diff --git a/.travis.yml b/.travis.yml index b4eddd2..790e401 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,21 @@ +sudo: false +os: + - osx + - linux + language: node_js node_js: - '8' dist: trusty + +before_install: + - if [ $TRAVIS_OS_NAME == "linux" ]; then + export CXX="g++-4.9" CC="gcc-4.9" DISPLAY=:99.0; + sh -e /etc/init.d/xvfb start; + sleep 3; + fi install: | - ./.travis/install.sh + ./.travis/install.sh script: | ./.travis/script.sh deploy: diff --git a/.travis/before_install.sh b/.travis/before_install.sh deleted file mode 100755 index a1ce32a..0000000 --- a/.travis/before_install.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -#-- script to automate preinstall, server compile, and package -# Exit on first error, print all commands. -set -ev -set -o pipefail - -if [$TRAVIS_OS_NAME == 'linux']; then - export CXX="g++-4.9" CC="gcc-4.9" DISPLAY=:99.0; - sh -e /etc/init.d/xvfb start; - sleep 3; -fi \ No newline at end of file diff --git a/.travis/script.sh b/.travis/script.sh index 7843f77..4860cea 100755 --- a/.travis/script.sh +++ b/.travis/script.sh @@ -6,10 +6,11 @@ set -o pipefail cd ./server npm run compile:server +npm test --silent cd ../client npm run package:vsix npm install -g vsce -npm test 2>&1 | tee +npm test --silent diff --git a/client/.vscodeignore b/client/.vscodeignore index 795e714..a044114 100755 --- a/client/.vscodeignore +++ b/client/.vscodeignore @@ -5,5 +5,6 @@ test/** src/** **/*.map .gitignore +.eslintignore tsconfig.json vsc-extension-quickstart.md diff --git a/client/package.json b/client/package.json index caaddd3..ca6f194 100755 --- a/client/package.json +++ b/client/package.json @@ -253,21 +253,51 @@ "postinstall": "node ./node_modules/vscode/bin/install", "package:vsix": "node ./node_modules/vsce/out/vsce package", "prepublish": "tsc -p ./", - "test": "node ./node_modules/vscode/bin/test" + "pretest": "npm run lint", + "test": "nyc node ./node_modules/vscode/bin/test --compilers ts:ts-node/register ./test/*.test.ts", + "tslint": "tslint", + "lint": "npm run tslint 'src/**/*.ts' 'test/extension.test.ts'" }, "devDependencies": { "@types/chai": "^3.5.2", "@types/mocha": "^2.2.33", "@types/node": "^6.0.52", + "angular-tslint-rules": "^1.2.1", "chai": "^4.1.2", "mocha": "^5.0.0", "nyc": "^11.4.1", "ts-node": "^4.1.0", + "tslint": "^5.9.1", + "tslint-loader": "^3.3.0", "typescript": "^2.6.2", "vsce": "^1.30.0", "vscode": "^1.1.5" }, "dependencies": { "vscode-languageclient": "^3.5.0" + }, + "nyc": { + "include": [ + "src/**/*.ts", + "test/data" + ], + "extension": [ + ".ts" + ], + "exclude": [ + "out/**", + "server/**", + "coverage/**" + ], + "reporter": [ + "text-summary", + "html" + ], + "all": true, + "check-coverage": true, + "statements": 0, + "branches": 0, + "functions": 0, + "lines": 0 } } diff --git a/client/src/extension.ts b/client/src/extension.ts index eb2b82c..fcdb842 100755 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -1,7 +1,16 @@ -/* -------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - * ------------------------------------------------------------------------------------------ */ +/* + * 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. + */ 'use strict'; import * as path from 'path'; @@ -15,76 +24,76 @@ import { LanguageClient, LanguageClientOptions, SettingMonitor, ServerOptions, T let client: LanguageClient = null; export function activate(context: ExtensionContext) { - //console.log('CLIENT activate!!!'); //debug + // console.log('CLIENT activate!!!'); - let disposable3 = workspace.onDidChangeConfiguration((params) => { - //console.log(`CLIENT onDidChangeConfiguration ${JSON.stringify(params)}`); //debug - //let conf = workspace.getConfiguration(); + const disposable3 = workspace.onDidChangeConfiguration((params) => { + // console.log(`CLIENT onDidChangeConfiguration ${JSON.stringify(params)}`); //debug + // let conf = workspace.getConfiguration(); }); context.subscriptions.push(disposable3); // The server is implemented in node - let serverModule = context.asAbsolutePath(path.join('server', 'server.js')); + const serverModule = context.asAbsolutePath(path.join('server', 'server.js')); // The debug options for the server - let debugOptions = { execArgv: ["--nolazy", "--inspect=6009"] }; + const debugOptions = { execArgv: ['--nolazy', '--inspect=6009'] }; // If the extension is launched in debug mode then the debug server options are used // Otherwise the run options are used - let serverOptions: ServerOptions = { + const serverOptions: ServerOptions = { run: { module: serverModule, transport: TransportKind.ipc }, debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions } - } + }; // Options to control the composer validator client - let clientOptions: LanguageClientOptions = { + const clientOptions: LanguageClientOptions = { // Register the server for composer documents documentSelector: ['composer', 'composer-acl', 'composer-qry'], synchronize: { // Synchronize the setting section 'Composer' to the server configurationSection: 'composer', // Notify the server about file changes to '.clientrc files contain in the workspace - //fileEvents: workspace.createFileSystemWatcher('**/.clientrc') + // fileEvents: workspace.createFileSystemWatcher('**/.clientrc') } - } + }; // Create the language client and start the client. client = new LanguageClient('Hyperledger Composer', serverOptions, clientOptions); - let disposable = client.start(); + const disposable = client.start(); client.onReady().then(() => { - // Push the disposable to the context's subscriptions so that the + // Push the disposable to the context's subscriptions so that the // client can be deactivated on extension deactivation context.subscriptions.push(disposable); - let disposable2 = window.onDidChangeActiveTextEditor((editor) => { + const disposable2 = window.onDidChangeActiveTextEditor((editor) => { if (!editor) { return; } - //make sure it is one of the languages we care about - if ((editor.document.languageId != "composer") && - (editor.document.languageId != "composer-acl") && - (editor.document.languageId != "composer-qry")) { + // make sure it is one of the languages we care about + if ((editor.document.languageId !== 'composer') && + (editor.document.languageId !== 'composer-acl') && + (editor.document.languageId !== 'composer-qry')) { return; } - //For now, force an update when the editor is changed and a new one is selected. - //This allows us to update properly in the event of referential integrity changes between files. - let params = client.code2ProtocolConverter.asChangeTextDocumentParams(editor.document); - let notification: NotificationType = new NotificationType('textDocument/didChange'); + // For now, force an update when the editor is changed and a new one is selected. + // This allows us to update properly in the event of referential integrity changes between files. + const params = client.code2ProtocolConverter.asChangeTextDocumentParams(editor.document); + const notification: NotificationType = new NotificationType('textDocument/didChange'); client.sendNotification(notification, params); }); context.subscriptions.push(disposable2); - //Register a request handler to catch 'composer.generateUML' requests from the server. - client.onRequest("openUML", async (docContent: string, originatingFileName: string) => { + // Register a request handler to catch 'composer.generateUML' requests from the server. + client.onRequest('openUML', async (docContent: string, originatingFileName: string) => { try { return await handleGenerateUml(docContent, originatingFileName); } catch (ex) { - console.log("CLIENT Exception:" + ex.message) + console.log('CLIENT Exception:' + ex.message); } }); - }) + }); } /** @@ -94,55 +103,55 @@ export function activate(context: ExtensionContext) { * note that this can be undefined if the command was activated by a keyboard shortcut! */ async function handleGenerateUml(docContent: string, originatingFileName: string) { - //if the diagram was created with a keyboard shortcut, the server does not get the doc name passed in - //so get the name here, if the active doc is a .cto file. - let currentEditor = window.activeTextEditor; - let targetViewColumn = undefined; + // if the diagram was created with a keyboard shortcut, the server does not get the doc name passed in + // so get the name here, if the active doc is a .cto file. + const currentEditor = window.activeTextEditor; + let targetViewColumn; if (currentEditor) { - //console.log("CLIENT current active editor: " + currentEditor.document.uri); - let activeUri = currentEditor.document.uri.toString(); + // console.log("CLIENT current active editor: " + currentEditor.document.uri); + const activeUri = currentEditor.document.uri.toString(); if (!originatingFileName || originatingFileName.length === 0) { - let activeUri = currentEditor.document.uri.toString() - if (activeUri.endsWith(".cto")) { + // let activeUri = currentEditor.document.uri.toString(); + if (activeUri.endsWith('.cto')) { originatingFileName = activeUri; } } - //always get the view column if we can + // always get the view column if we can if (originatingFileName === activeUri) { targetViewColumn = currentEditor.viewColumn; } } - //get config info we need to set flags - let allConfig = workspace.getConfiguration(); - let keepSrcFileOpen = allConfig.get('composer.UML.keepSourceFileOpen'); + // get config info we need to set flags + const allConfig = workspace.getConfiguration(); + const keepSrcFileOpen = allConfig.get('composer.UML.keepSourceFileOpen'); let autoShowDiagam = allConfig.get('composer.UML.autoShowDiagam'); - let diagramTheme = allConfig.get('composer.UML.diagramTheme'); + const diagramTheme = allConfig.get('composer.UML.diagramTheme'); - //if we are to try and show the diagram, we need the plantUML extention installed. + // if we are to try and show the diagram, we need the plantUML extention installed. if (autoShowDiagam) { - //detect install of plantUML extention. - const ext = extensions.getExtension("jebbs.plantuml"); + // detect install of plantUML extention. + const ext = extensions.getExtension('jebbs.plantuml'); if (!ext) { await window.showErrorMessage("The 'jebbs.plantuml' extention must be installed and configured to view the UML diagram."); - //TODO auto install extension. + // TODO auto install extension. /*await window.showErrorMessage("The 'plantuml' extention must be installed and configured. Install?", { title: "Install", action: "install", }).then(handleInstallResponse); */ - //turn off diagram drawing as we do not have the extention installed + // turn off diagram drawing as we do not have the extention installed autoShowDiagam = false; } else { - //we have the extention, turn off auto updating - //console.log("Client: plantuml ext-path: " + ext.extensionPath); //debug - let fileType = allConfig.get('plantuml.previewFileType'); - let autoUpdate = allConfig.get('plantuml.previewAutoUpdate'); - if (autoUpdate || (fileType !== "svg")) { - //force plantUML to turn off autoUpdate as it causes problems when other files sre changed - //also set fileType to 'svg' as the default 'png' drops the RHS of wide diagrams. - let configPlantUml = allConfig['plantuml']; + // we have the extention, turn off auto updating + // console.log("Client: plantuml ext-path: " + ext.extensionPath); //debug + const fileType = allConfig.get('plantuml.previewFileType'); + const autoUpdate = allConfig.get('plantuml.previewAutoUpdate'); + if (autoUpdate || (fileType !== 'svg')) { + // force plantUML to turn off autoUpdate as it causes problems when other files sre changed + // also set fileType to 'svg' as the default 'png' drops the RHS of wide diagrams. + const configPlantUml = allConfig['plantuml']; configPlantUml.previewAutoUpdate = false; configPlantUml.previewFileType = 'svg'; if (diagramTheme === 'blue') { @@ -150,90 +159,90 @@ async function handleGenerateUml(docContent: string, originatingFileName: string } } - //Note: This looks like it should work but does not. TODO: Raise vscode issue - //allConfig.update('plantuml.previewAutoUpdate',false,false); + // Note: This looks like it should work but does not. TODO: Raise vscode issue + // allConfig.update('plantuml.previewAutoUpdate',false,false); } } - //construct temp file name - var fileName = os.tmpdir() + path.sep + "composer.puml"; - var umlDocUri = Uri.file(fileName) + // construct temp file name + const fileName = os.tmpdir() + path.sep + 'composer.puml'; + const umlDocUri = Uri.file(fileName); - //make sure file exists - needed as a workaround to vscode issue #29156 + // make sure file exists - needed as a workaround to vscode issue #29156 if (!fs.existsSync(fileName)) { - fs.writeFileSync(fileName, ""); + fs.writeFileSync(fileName, ''); } - //open file - contents will always be replaced later on. - let document = await workspace.openTextDocument(umlDocUri); + // open file - contents will always be replaced later on. + const document = await workspace.openTextDocument(umlDocUri); - //show doc to the user - let options: TextDocumentShowOptions = { + // show doc to the user + const options: TextDocumentShowOptions = { preserveFocus: false, preview: true, viewColumn: ViewColumn.One - } - let textEditor = await window.showTextDocument(document, options); + }; + const textEditor = await window.showTextDocument(document, options); return await textEditor.edit(async (editBuilder) => { - //edit doc to replace all doc content with new PlantUML syntax - var lastLineLength = document.lineAt(document.lineCount - 1).text.length; + // edit doc to replace all doc content with new PlantUML syntax + const lastLineLength = document.lineAt(document.lineCount - 1).text.length; editBuilder.replace(new Range(new Position(0, 0), new Position(textEditor.document.lineCount - 1, lastLineLength)), docContent); - }).then(async editApplied => { + }).then(async (editApplied) => { if (!editApplied) { - console.log("Client could not apply edit"); + console.log('Client could not apply edit'); return; } - //save the file whilst it's the active one - var saved = await document.save(); + // save the file whilst it's the active one + const saved = await document.save(); if (!saved) { - console.log("Client could not save doc: " + umlDocUri.toString()); + console.log('Client could not save doc: ' + umlDocUri.toString()); } - let result = undefined; + let result; if (autoShowDiagam) { try { result = await commands.executeCommand('plantuml.preview'); } catch (ex) { - console.log("CLIENT error: " + ex); - return await window.showErrorMessage("" + ex); + console.log('CLIENT error: ' + ex); + return await window.showErrorMessage('' + ex); } } if (result !== undefined) { - //console.log("Client preview returned: " + result); //debug + // console.log("Client preview returned: " + result); //debug } - //check for option to close the composer.puml file + // check for option to close the composer.puml file if (!keepSrcFileOpen) { - //make sure we are closing the correct window, just in case + // make sure we are closing the correct window, just in case if (window.activeTextEditor) { if (window.activeTextEditor.document.uri.toString() === umlDocUri.toString()) { - //console.log("CLIENT closing: " + window.activeTextEditor.document.uri) - //Note that this can still go wrong sometimes and close the wrong window, - //looks like a missing .then() in plantuml. + // console.log("CLIENT closing: " + window.activeTextEditor.document.uri) + // Note that this can still go wrong sometimes and close the wrong window, + // looks like a missing .then() in plantuml. await commands.executeCommand('workbench.action.closeActiveEditor'); } else { - console.log("CLIENT: could not close ActiveTextEditor: wrong URI: " + window.activeTextEditor.document.uri.toString() + " : " + umlDocUri.toString()); + console.log('CLIENT: could not close ActiveTextEditor: wrong URI: ' + window.activeTextEditor.document.uri.toString() + ' : ' + umlDocUri.toString()); } } else { - //console.log("CLIENT: could not close window - no ActiveTextEditor"); + // console.log("CLIENT: could not close window - no ActiveTextEditor"); } } else { - //move cursor to top in composer.puml file to clear the selection of the whole doc - //that is present by default (as we replaced all the text in the doc). + // move cursor to top in composer.puml file to clear the selection of the whole doc + // that is present by default (as we replaced all the text in the doc). await commands.executeCommand('cursorTop'); } - //reset the correct cto editor as active if we are showing the diagram - //otherwise let the composer.puml file have focus + // reset the correct cto editor as active if we are showing the diagram + // otherwise let the composer.puml file have focus if (autoShowDiagam) { - //Note that the visibleTextEditors list is the nost accurate as it contains - //the correct view column. However, we're not always present in this list - //and I'm not sure why, but we always seem to be in the textDocuments - //list so we try both. + // Note that the visibleTextEditors list is the nost accurate as it contains + // the correct view column. However, we're not always present in this list + // and I'm not sure why, but we always seem to be in the textDocuments + // list so we try both. for (const editor of window.visibleTextEditors) { - //console.log("CLIENT visible editor: " + editor.document.uri.toString()); + // console.log("CLIENT visible editor: " + editor.document.uri.toString()); if (editor.document.uri.toString() === originatingFileName) { await window.showTextDocument(editor.document, editor.viewColumn); return; @@ -241,28 +250,26 @@ async function handleGenerateUml(docContent: string, originatingFileName: string } for (const editor of workspace.textDocuments) { - //console.log("CLIENT visible TEXTdOC: " + editor.uri.toString()); + // console.log("CLIENT visible TEXTdOC: " + editor.uri.toString()); if (editor.uri.toString() === originatingFileName) { - //note that targetViewColumn may be undefined, but that's OK - it will default to ViewColumn.One + // note that targetViewColumn may be undefined, but that's OK - it will default to ViewColumn.One await window.showTextDocument(editor, targetViewColumn); return; } } } - //Note, if we ever find ourselves here and keepSrcFileOpen is false, - //then callimg navigateBack 3 times does seem to work to reset the cto editor, - //but that feels too hacky for now... - //return await commands.executeCommand('workbench.action.navigateBack') + // Note, if we ever find ourselves here and keepSrcFileOpen is false, + // then callimg navigateBack 3 times does seem to work to reset the cto editor, + // but that feels too hacky for now... + // return await commands.executeCommand('workbench.action.navigateBack') }); - - } function handleInstallResponse(options) { console.log(`CLIENT installHandler: ${JSON.stringify(options)}`); - var cmd; + let cmd; if (os.platform() === 'win32') { cmd = spawn('code.cmd', ['--install-extension', 'jebbs.plantuml']); } else { @@ -270,6 +277,6 @@ function handleInstallResponse(options) { } cmd.stdout.on('data', (data) => { window.showInformationMessage(data.toString()); }); cmd.stderr.on('data', (data) => { window.showErrorMessage(data.toString()); }); - cmd.on('close', (code) => { console.log('Fin:' + code.toString()) }); - //todo reset vscode editor! -} \ No newline at end of file + cmd.on('close', (code) => { console.log('Fin:' + code.toString()); }); + // todo reset vscode editor! +} diff --git a/client/test/data/permissions.acl b/client/test/data/invalid/invalid.txt similarity index 100% rename from client/test/data/permissions.acl rename to client/test/data/invalid/invalid.txt diff --git a/client/test/data/valid/acl/permissions.acl b/client/test/data/valid/acl/permissions.acl new file mode 100644 index 0000000..8ac8d49 --- /dev/null +++ b/client/test/data/valid/acl/permissions.acl @@ -0,0 +1,34 @@ +/** + * Access Control List for the bond data + */ +rule Issuer { + description: "Allow full access to the issuer of a bond" + participant(i): "org.acme.bond.Issuer" + operation: ALL + resource(a): "org.acme.bond.BondAsset" + condition: (a.bond.issuer.memberId === i.memberId) + action: ALLOW +} + +rule Default { + description: "Allow read access" + participant: "org.acme.bond.*" + operation: ALL + resource: "org.acme.bond.*" + action: ALLOW +} + +rule AllAccess1 { + description: "Description of the Basic ACL rule" + participant: "org.hyperledger.composer.system.Participant" + operation: ALL + resource: "org.hyperledger.composer.system.**" + action: ALLOW +} +rule AllAccess2{ + description: "Description of the Basic ACL rule" + participant: "org.hyperledger.composer.system.Participant" + operation: ALL + resource: "org.acme.bond.**" + action: ALLOW +} \ No newline at end of file diff --git a/client/test/data/valid/cto/bond.cto b/client/test/data/valid/cto/bond.cto new file mode 100644 index 0000000..2d60f4c --- /dev/null +++ b/client/test/data/valid/cto/bond.cto @@ -0,0 +1,93 @@ +/** + * Definition of a Bond, based on the FpML schema: + * http://www.fpml.org/spec/fpml-5-3-2-wd-2/html/reporting/schemaDocumentation/schemas/fpml-asset-5-3_xsd/elements/bond.html + * + */ +namespace org.acme.bond + +enum CouponType { + o FIXED + o FLOATING +} + +participant Member identified by memberId { + o String memberId + o String name + o String lastName optional +} + +participant Issuer extends Member { + +} + +enum PeriodEnum { + o DAY + o WEEK + o MONTH + o YEAR +} + +concept PaymentFrequency { + o Integer periodMultiplier + o PeriodEnum period +} + +concept Bond { + o String[] instrumentId + o String description optional + o String currency optional + o String[] exchangeId + o String clearanceSystem optional + o String definition optional + o String seniority optional + o CouponType couponType optional + o Double couponRate optional + o Long dayCount optional + o Boolean isMatured optional + o DateTime maturity + o Double parValue + o Double faceAmount + o PaymentFrequency paymentFrequency + o String dayCountFraction + --> Issuer issuer + --> Issuer[] owners optional +} + +asset BondAsset identified by ISINCode { + o String ISINCode + o Bond bond +} + +transaction PublishBond { + o String ISINCode + o Bond bond +} + +event BondEvent { + o String prop1 + o String prop2 +} + +transaction EmitBondEvent { + +} + +transaction EmitMultipleBondEvents { + +} + +abstract asset BaseAsset { + +} + +abstract concept BaseConcept { + +} + +abstract participant BaseParticipant { + +} + +abstract transaction BaseTransaction { + +} \ No newline at end of file diff --git a/client/test/data/test.cto b/client/test/data/valid/cto/test.cto similarity index 100% rename from client/test/data/test.cto rename to client/test/data/valid/cto/test.cto diff --git a/client/test/data/valid/qry/queries.qry b/client/test/data/valid/qry/queries.qry new file mode 100644 index 0000000..f0755b8 --- /dev/null +++ b/client/test/data/valid/qry/queries.qry @@ -0,0 +1,106 @@ +query findBondAboveAFaceAmount { + description: "Find all bonds with a face amount greater than _$faceAmount" + statement: SELECT org.acme.bond.BondAsset WHERE (bond.faceAmount > _$faceAmount) +} +query findBondByFaceAmountNotAboveAValue { + description: "Find all bonds with a face amount not greater than _$faceAmount" + statement: SELECT org.acme.bond.BondAsset WHERE(_$faceAmount>=bond.faceAmount) +} +query findBondByDayCount { + description: "Find all bonds with a specific day count _$dayCount" + statement: SELECT org.acme.bond.BondAsset WHERE(bond.dayCount==_$dayCount) +} +query findBondAboveDayCount { + description: "Find all bonds with day count greater than _$dayCount" + statement: SELECT org.acme.bond.BondAsset WHERE(bond.dayCount>_$dayCount) +} +query findBondLessThanDayCount { + description: "Find all bonds with day count less than _$dayCount" + statement: SELECT org.acme.bond.BondAsset WHERE(bond.dayCount<_$dayCount) +} +query findBondByPaymentFrequencyPeriod { + description: "Find all bonds with a payment frequecy period _$period" + statement: SELECT org.acme.bond.BondAsset WHERE (bond.paymentFrequency.period == _$period) +} +query findBondAboveAPaymentFrequencyPeriodMultiplierValue { + description: "Find all bonds with a payment frequecy period multiplier greater than _$multiplier" + statement: SELECT org.acme.bond.BondAsset WHERE (bond.paymentFrequency.periodMultiplier > _$multiplier) +} +query findBondByPaymentFrequencyPeriodMultiplierNotAboveAValue { + description: "Find all bonds with a payment frequecy period multiplier not greater than _$multiplier" + statement: SELECT org.acme.bond.BondAsset WHERE (bond.paymentFrequency.periodMultiplier <= _$multiplier) +} +query findBondByIsMatured { + description: "Find all bonds by isMatured _$isMatured" + statement: SELECT org.acme.bond.BondAsset WHERE (bond.isMatured == _$isMatured) +} +query findBondBeforeMaturity { + description: "Find all bonds before a maturity _$maturity" + statement: SELECT org.acme.bond.BondAsset WHERE (bond.maturity <= _$maturity) +} +query findBondAfterMaturity { + description: "Find all bonds after a maturity _$maturity" + statement: SELECT org.acme.bond.BondAsset WHERE (bond.maturity > _$maturity) +} +query findBondByCurrency { + description: "Find all bonds by a specific currency _$currency" + statement: SELECT org.acme.bond.BondAsset WHERE (bond.currency == _$currency) +} +query findBondByIsMaturedBeforeMaturity { + description: "Find all bonds that are either matured or before a maturity _$maturity" + statement: SELECT org.acme.bond.BondAsset WHERE (bond.isMatured == true OR bond.maturity < _$maturity) +} +query findBondByTheIsMaturedAndCurrencyORDayCount { + description: "Find all bonds by a specific currency _$currency and the number days." + statement: SELECT org.acme.bond.BondAsset WHERE ((bond.isMatured == true AND bond.currency == _$currency) OR bond.dayCount <= _$dayCount ) +} +query findBondByCurrencyORTheDayCountAndMaturity { + description: "Find all bonds by a specific currency _$currency or the number days _$dayCount and after a maturity date:_$maturity" + statement: SELECT org.acme.bond.BondAsset WHERE (bond.currency == _$currency OR (bond.dayCount <= _$dayCount AND bond.maturity > _$maturity)) +} +query findBondByTheCurrencyOrDayCountANDMaturity { + description: "Find all bonds by a specific currency _$currency or the number days _$dayCount and after a maturity date:_$maturity" + statement: SELECT org.acme.bond.BondAsset WHERE ((bond.currency == _$currency OR bond.dayCount <= _$dayCount) AND bond.maturity > _$maturity) +} +query findBondByCurrencyANDTheDayCountOrMaturity { + description: "Find all bonds by a specific currency _$currency and the number days _$dayCount or after a maturity date:_$maturity" + statement: SELECT org.acme.bond.BondAsset WHERE (bond.currency == _$currency AND (bond.dayCount <= _$dayCount OR bond.maturity > _$maturity)) +} +query findBondByCurrencyOrDayCountOrMaturity { + description: "Find all bonds by a specific currency _$currency and the number days _$dayCount or after a maturity date:_$maturity" + statement: SELECT org.acme.bond.BondAsset WHERE (bond.currency == _$currency OR bond.dayCount <= _$dayCount OR bond.maturity > _$maturity) +} +query findBondByCurrencyAndDayCountAndMaturity { + description: "Find all bonds by a specific currency _$currency and the number days _$dayCount and after a maturity date:_$maturity" + statement: SELECT org.acme.bond.BondAsset WHERE (bond.currency == _$currency AND bond.dayCount <= _$dayCount AND bond.maturity > _$maturity) +} +query findBondByCurrencyAndDayCountAndMaturityAndMultiplier{ + description: "Find all bonds by a specific currency _$currency and the number days _$dayCount and after a maturity date:_$maturity and multiplier" + statement: SELECT org.acme.bond.BondAsset WHERE (bond.currency == _$currency AND bond.dayCount <= _$dayCount AND bond.maturity > _$maturity AND bond.paymentFrequency.periodMultiplier <= _$periodMultiplier) +} + +query findBondByCurrencyORTheDayCountAndMaturityORMultiplier { + description: "Find all bonds by a specific currency _$currency or the number days _$dayCount and after a maturity date:_$maturity OR with no more than a specific multiplier" + statement: SELECT org.acme.bond.BondAsset WHERE (bond.currency == _$currency OR bond.dayCount <= _$dayCount AND bond.maturity > _$maturity OR bond.paymentFrequency.periodMultiplier <= _$periodMultiplier) +} + +query findBondByCurrencyAndUnsupportedType { + description: "Find all bonds by a specific currency _$currency and an instrumentId" + statement: SELECT org.acme.bond.BondAsset WHERE (bond.currency == _$currency AND bond.instrumentId <= _$instrumentId) +} +query findBondByExchangeIdUnsupported { + description: "Find all bonds with a set of exchange id _$exchangeId is unsupported" + statement: SELECT org.acme.bond.BondAsset WHERE (bond.exchangeId == _$exchangeId) +} +query findIssuerById { + description: "Find all issuer by memberId _$memberId" + statement: SELECT org.acme.bond.Issuer WHERE (memberId == _$memberId) +} +query findIssuerByName { + description: "Find all issuer by name _$name" + statement: SELECT org.acme.bond.Issuer WHERE (name == _$name) +} +query findTxnByTransactionType { + description: "Find all Transaction by transaction type" + statement: SELECT org.hyperledger.composer.system.HistorianRecord WHERE (transactionType == 'org.acme.bond.PublishBond') +} \ No newline at end of file diff --git a/client/test/extension.test.ts b/client/test/extension.test.ts index a73c889..ab0b668 100755 --- a/client/test/extension.test.ts +++ b/client/test/extension.test.ts @@ -1,13 +1,17 @@ -// -// Note: This example test is leveraging the Mocha test framework. -// Please refer to their documentation on https://mochajs.org/ for help. -// - -// The module 'assert' provides assertion methods from node +/* + * 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. + */ import * as assert from 'assert'; - -// You can import and use all API from the 'vscode' module -// as well as import your extension to test it import * as vscode from 'vscode'; import * as path from 'path'; import * as myExtension from '../src/extension'; @@ -15,33 +19,31 @@ import { ExtensionContext, workspace, Uri } from 'vscode'; import { error } from 'util'; // Defines a Mocha test suite to group tests of similar kind together -suite("Extension Tests", () => { +suite('Extension Tests', () => { + const rootPath = path.dirname(__dirname); + // open a cto document should return the expected document id and line count + test('activate should return a cto document when open a cto file', () => { - // test validation cto - test("activate", () => { - - // let context: ExtensionContext; - // let serverModule = context.asAbsolutePath(path.join('../server', 'server.js')); + // const rootPath = path.dirname(__dirname); + const uri = vscode.Uri.file(path.join(rootPath, '../test/data/valid/cto/test.cto')); - let uri = vscode.Uri.file(path.join("/Users/Fenglian/dev/git/composer-vscode-plugin/client/test/data", 'test.cto')); - - // let uri = vscode.Uri.file(path.join(vscode.workspace.rootPath || '', './data/test.cto')); - console.log('uri' + uri); - workspace.openTextDocument(uri).then((document) =>{ + workspace.openTextDocument(uri).then((document) => { + const text = document.getText(); + assert.equal(document.languageId, 'composer'); + assert.ok(document.lineCount === 41); - let text = document.getText(); - console.log('text = ' + text); + }); + }); - }, (err) =>{ - assert.ok(false, `error in OpenTextDocument ${err}`); - return Promise.reject(err); - }); + test('activate should return an acl file when open an acl file', () => { - // let serverOptions: ServerOptions = { - // run: { module: serverModule, transport: TransportKind.ipc } - // } - console.log('is activate called?'); + const uri = vscode.Uri.file(path.join(rootPath, '../test/data/valid/acl/permissions.acl')); - }); -}); \ No newline at end of file + workspace.openTextDocument(uri).then((document) => { + const text = document.getText(); + assert.equal(document.languageId, 'composer-acl'); + assert.ok(document.lineCount === 34); + }); + }); +}); diff --git a/client/tsconfig.json b/client/tsconfig.json index 79f9b25..42e8307 100755 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -1,9 +1,9 @@ { "compilerOptions": { - //"noUnusedLocals": true, - //"noUnusedParameters": true, - //"noImplicitAny": true, - //"noImplicitReturns": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noImplicitAny": true, + // "noImplicitReturns": true, "target": "es6", "module": "commonjs", "moduleResolution": "node", diff --git a/client/tslint.json b/client/tslint.json new file mode 100644 index 0000000..ae99cc8 --- /dev/null +++ b/client/tslint.json @@ -0,0 +1,61 @@ +{ + "extends": [ + "tslint:recommended" + ], + "rules": { + "trailing-comma": [ + false, + { + "multiline": "always", + "singleline": "never" + } + ], + "rulesDirectory": [ + "./node_modules/codelyzer" + ], + "interface-name": [ + false, + "always-prefix" + ], + "indent": [ + true, + "spaces" + ], + "array-type": false, + "prefer-for-of": false, + "member-access": false, + "no-console": [ + true, + "time", + "timeEnd", + "trace" + ], + "forin": false, + "max-line-length": false, + "no-string-literal": false, + "no-use-before-declare": true, + "object-literal-sort-keys": false, + "object-literal-shorthand": false, + "ordered-imports": false, + "quotemark": [ + true, + "single", + "avoid-escape" + ], + "variable-name": [ + true, + "allow-leading-underscore", + "allow-pascal-case", + "ban-keywords", + "check-format" + ], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ] + } +} diff --git a/server/License.txt b/server/License.txt index 027ef92..6303f09 100755 --- a/server/License.txt +++ b/server/License.txt @@ -1,11 +1,11 @@ -Copyright (c) Microsoft Corporation +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 -All rights reserved. +http://www.apache.org/licenses/LICENSE-2.0 -MIT License - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +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. diff --git a/server/package.json b/server/package.json index ce04788..8168912 100755 --- a/server/package.json +++ b/server/package.json @@ -23,21 +23,31 @@ "chai": "^4.1.2", "mocha": "^5.0.0", "nyc": "^11.4.1", + "source-map-support": "^0.5.3", "ts-node": "^4.1.0", + "tslint": "^5.9.1", + "tslint-loader": "^3.5.3", "typescript": "^2.6.2" }, "scripts": { "install:server": "installServerIntoExtension ../client ./package.json ./tsconfig.json", "compile:server": "installServerIntoExtension ../client ./package.json ./tsconfig.json && tsc -p .", "watch:server": "installServerIntoExtension ../client ./package.json ./tsconfig.json && tsc -w -p .", + "pretest": "npm run lint", + "tslint": "tslint", + "lint": "npm run tslint 'src/**/*.ts' './test/server.spec.ts'", "test": "nyc ./node_modules/.bin/mocha --compilers ts:ts-node/register ./test/*.spec.ts" }, "nyc": { + "extension": [ + ".ts" + ], + "require": [ + "ts-node/register" + ], "exclude": [ - "coverage/**", - "out/**", - "scripts/**", - "test/**" + "test/**", + "coverage/**" ], "reporter": [ "text-summary", @@ -45,9 +55,9 @@ ], "all": true, "check-coverage": true, - "statements": 100, - "branches": 100, - "functions": 100, - "lines": 100 + "statements": 0, + "branches": 0, + "functions": 0, + "lines": 0 } } diff --git a/server/src/server.ts b/server/src/server.ts index 6577774..2e0f590 100755 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -1,7 +1,16 @@ -/* -------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - * ------------------------------------------------------------------------------------------ */ +/* + * 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. + */ 'use strict'; import { @@ -12,41 +21,41 @@ import { CompletionItem, CompletionItemKind } from 'vscode-languageserver'; -//import { GenericNotificationHandler } from 'vscode-jsonrpc'; -import Uri from 'vscode-uri' + // import { GenericNotificationHandler } from 'vscode-jsonrpc'; +import Uri from 'vscode-uri'; import { ModelManager, AclManager, QueryManager, ModelFile, ModelUtil, AclFile, Logger, FileWriter, Writer } from 'composer-common'; -import PlantUMLVisitor = require("composer-common/lib/codegen/fromcto/plantuml/plantumlvisitor"); +import PlantUMLVisitor = require('composer-common/lib/codegen/fromcto/plantuml/plantumlvisitor'); -//First, before calling any composer code, -//turn off all composer logging for now until the base fixes errors if log folder cannot be created! + // First, before calling any composer code, + // turn off all composer logging for now until the base fixes errors if log folder cannot be created! Logger.setFunctionalLogger({ log: () => { - //none, zip, nada! + // none, zip, nada! } }); -//create the three main singleton managers we need to handle all -//open *.cto, .qry and permissions.acl documents in the workspace. -//note that composer currently only supports one query and one acl file at present -let modelManager = new ModelManager(); -let aclManager = new AclManager(modelManager); -let queryManager = new QueryManager(modelManager); +// create the three main singleton managers we need to handle all +// open *.cto, .qry and permissions.acl documents in the workspace. +// note that composer currently only supports one query and one acl file at present +const modelManager = new ModelManager(); +const aclManager = new AclManager(modelManager); +const queryManager = new QueryManager(modelManager); // Create a connection for the server. The connection uses Node's IPC as a transport -let connection: IConnection = createConnection(new IPCMessageReader(process), new IPCMessageWriter(process)); +const connection: IConnection = createConnection(new IPCMessageReader(process), new IPCMessageWriter(process)); // Create a simple text document manager. The text document manager // supports full document sync only -let documents: TextDocuments = new TextDocuments(); +const documents: TextDocuments = new TextDocuments(); // Make the text document manager listen on the connection // for open, change and close text document events documents.listen(connection); // After the server has started the client sends an initialize request. The server receives -// in the passed params the rootPath of the workspace plus the client capabilities. +// in the passed params the rootPath of the workspace plus the client capabilities. let workspaceRoot: string; connection.onInitialize((params): InitializeResult => { - //connection.console.log(`SERVER onInitialize ${JSON.stringify(params)}`); //debug + // connection.console.log(`SERVER onInitialize ${JSON.stringify(params)}`); //debug workspaceRoot = params.rootPath; return { capabilities: { @@ -58,29 +67,29 @@ connection.onInitialize((params): InitializeResult => { // completionProvider: { // resolveProvider: false // } - //lots more providers can be added here... + // lots more providers can be added here... executeCommandProvider: { commands: [ 'composer.generateUML' ] } } - } + }; }); // The content of a text document has changed. This event is emitted // when the text document first opened or when its content has changed. documents.onDidChangeContent((doc) => { - //connection.console.log("SERVER onDidChangeContent: " + doc.document.uri); //debug + // connection.console.log("SERVER onDidChangeContent: " + doc.document.uri); //debug validateTextDocument(doc.document); }); documents.onDidOpen((doc) => { - //connection.console.log("SERVER onDidOpen: " + doc.document.uri); //debug + // connection.console.log("SERVER onDidOpen: " + doc.document.uri); //debug }); documents.onDidClose((doc) => { - //connection.console.log("SERVER onDidClose: " + doc.document.uri); //debug + // connection.console.log("SERVER onDidClose: " + doc.document.uri); //debug closeTextDocument(doc.document); }); @@ -91,26 +100,26 @@ interface Settings { // These are the settings defined in the client's package.json file. interface ComposerSettings { - contributor: boolean - maxNumberOfProblems: number + contributor: boolean; + maxNumberOfProblems: number; UML: { keepSourceFileOpen: boolean autoShowDiagam: boolean includeSystemNamespace: string diagramStyle: string diagramTheme: string - } + }; } // hold the settings let options: ComposerSettings; -// The settings have changed. Is sent on server activation as well. +// The settings have changed. Is sent on server activation as well. connection.onDidChangeConfiguration((change) => { - //connection.console.log(`SERVER onDidChangeConfiguration ${JSON.stringify(change)}`); //debug - options = change.settings.composer; + // connection.console.log(`SERVER onDidChangeConfiguration ${JSON.stringify(change)}`); //debug + options = change.settings.composer as ComposerSettings; options.maxNumberOfProblems = options.maxNumberOfProblems || 10; - //TODO set options.contributor flag on the model manager once the composer base team add the method. + // TODO set options.contributor flag on the model manager once the composer base team add the method. // Revalidate any open text documents documents.all().forEach(validateTextDocument); @@ -121,27 +130,27 @@ connection.onDidChangeConfiguration((change) => { * @param {ExecuteCommandParams} params - info passed from client - contains file and event names */ connection.onExecuteCommand((params) => { - //connection.console.log(`SERVER onExecuteCommand ${JSON.stringify(params)}`); //debug + // connection.console.log(`SERVER onExecuteCommand ${JSON.stringify(params)}`); //debug - //sanity check - if (params.command != 'composer.generateUML') { + // sanity check + if (params.command !== 'composer.generateUML') { return; } - //create a title from the event info - let diagramTitle = "Business Network Definition" //sensible default - let originatingFileName = ""; - //Note: if the command is launched from the keyboard shortcut or the command palette, - //we will not have any arguments passed in so cannot get the filename to use in the diagram. + // create a title from the event info + let diagramTitle = 'Business Network Definition'; // sensible default + let originatingFileName = ''; + // Note: if the command is launched from the keyboard shortcut or the command palette, + // we will not have any arguments passed in so cannot get the filename to use in the diagram. if (params.arguments.length >= 1) { if (params.arguments[0].external) { originatingFileName = params.arguments[0].external; if (originatingFileName.length > 0) { - //originatingFileName will be url encoded, so parse and decode before using - let originatingFileNameUri = Uri.parse(originatingFileName); - let path = originatingFileNameUri.path; + // originatingFileName will be url encoded, so parse and decode before using + const originatingFileNameUri = Uri.parse(originatingFileName); + const path = originatingFileNameUri.path; if (path && path.length > 0) { - let end = path.lastIndexOf('/'); + const end = path.lastIndexOf('/'); if (end !== -1) { diagramTitle += " for '" + path.substring(end + 1) + "'"; } @@ -160,87 +169,87 @@ connection.onExecuteCommand((params) => { * note that this can be undefined if the command was activated by a keyboard shortcut! */ function handleGenerateUml(diagramTitle: string, originatingFileName: string) { - //setup the visitor that walks the model - let visitor = new PlantUMLVisitor(); - let writer = new Writer(); - let parameters = { + // setup the visitor that walks the model + const visitor = new PlantUMLVisitor(); + const writer = new Writer(); + const parameters = { fileWriter: writer }; - //get info about classes to include + // get info about classes to include let result = []; const modelFiles = modelManager.getModelFiles(); for (let n = 0; n < modelFiles.length; n++) { const modelFile: ModelFile = modelFiles[n]; - //we exclude models from the system namespace by default - if (options.UML.includeSystemNamespace === "all") { + // we exclude models from the system namespace by default + if (options.UML.includeSystemNamespace === 'all') { result = result.concat(modelFile.getAllDeclarations()); - } else if (modelFile.getNamespace() != ModelUtil.getSystemNamespace()) { + } else if (modelFile.getNamespace() !== ModelUtil.getSystemNamespace()) { result = result.concat(modelFile.getAllDeclarations()); } } - //begin UML definition and global defines - parameters.fileWriter.writeLine(0, "@startuml composer"); + // begin UML definition and global defines + parameters.fileWriter.writeLine(0, '@startuml composer'); parameters.fileWriter.writeLine(0, "'** Auto generated content, any changes may be lost **'"); parameters.fileWriter.writeLine(0, "!define DATE %date[EEE, MMM d, ''yy 'at' HH:mm]%"); if (options.UML.diagramTheme === 'yellow') { - parameters.fileWriter.writeLine(0, "skinparam titleBackgroundColor LightYellow"); + parameters.fileWriter.writeLine(0, 'skinparam titleBackgroundColor LightYellow'); } else { - parameters.fileWriter.writeLine(0, "'AutoInclude") //include the blue style - parameters.fileWriter.writeLine(0, "skinparam titleBackgroundColor AliceBlue"); + parameters.fileWriter.writeLine(0, "'AutoInclude"); // include the blue style + parameters.fileWriter.writeLine(0, 'skinparam titleBackgroundColor AliceBlue'); } - parameters.fileWriter.writeLine(0, "skinparam titleBorderThickness 0.5"); - parameters.fileWriter.writeLine(0, "skinparam titleBorderRoundCorner 6"); - parameters.fileWriter.writeLine(0, "skinparam titleFontColor Black"); - parameters.fileWriter.writeLine(0, "skinparam titleFontSize 18"); + parameters.fileWriter.writeLine(0, 'skinparam titleBorderThickness 0.5'); + parameters.fileWriter.writeLine(0, 'skinparam titleBorderRoundCorner 6'); + parameters.fileWriter.writeLine(0, 'skinparam titleFontColor Black'); + parameters.fileWriter.writeLine(0, 'skinparam titleFontSize 18'); if (options.UML.diagramStyle === 'handwritten') { - parameters.fileWriter.writeLine(0, "skinparam handwritten true") + parameters.fileWriter.writeLine(0, 'skinparam handwritten true'); } else if (options.UML.diagramStyle === 'monochrome') { - parameters.fileWriter.writeLine(0, "skinparam monochrome true"); + parameters.fileWriter.writeLine(0, 'skinparam monochrome true'); } else if (options.UML.diagramStyle === 'monochrome-reverse') { - parameters.fileWriter.writeLine(0, "skinparam monochrome reverse"); + parameters.fileWriter.writeLine(0, 'skinparam monochrome reverse'); } - //add in title sequence - parameters.fileWriter.writeLine(0, "title"); + // add in title sequence + parameters.fileWriter.writeLine(0, 'title'); parameters.fileWriter.writeLine(0, diagramTitle); - parameters.fileWriter.writeLine(0, "end title"); + parameters.fileWriter.writeLine(0, 'end title'); - //add in the main body of the diagram - the classes info + // add in the main body of the diagram - the classes info result.forEach((decl) => { decl.accept(visitor, parameters); }); - if (options.UML.includeSystemNamespace === "none") { - //skip system namespace artifacts. Note that we can only hide classes that already exist, - //so for now simply search for the relevant string to check for existance. This is - //not a failsafe solution but should work well enough for now. - let model: string = writer.getBuffer(); - if (model.includes("org.hyperledger.composer.system.Asset")) { - parameters.fileWriter.writeLine(0, "hide org.hyperledger.composer.system.Asset"); + if (options.UML.includeSystemNamespace === 'none') { + // skip system namespace artifacts. Note that we can only hide classes that already exist, + // so for now simply search for the relevant string to check for existance. This is + // not a failsafe solution but should work well enough for now. + const model: string = writer.getBuffer(); + if (model.includes('org.hyperledger.composer.system.Asset')) { + parameters.fileWriter.writeLine(0, 'hide org.hyperledger.composer.system.Asset'); } - if (model.includes("org.hyperledger.composer.system.Participant")) { - parameters.fileWriter.writeLine(0, "hide org.hyperledger.composer.system.Participant"); + if (model.includes('org.hyperledger.composer.system.Participant')) { + parameters.fileWriter.writeLine(0, 'hide org.hyperledger.composer.system.Participant'); } - if (model.includes("org.hyperledger.composer.system.Transaction")) { - parameters.fileWriter.writeLine(0, "hide org.hyperledger.composer.system.Transaction"); + if (model.includes('org.hyperledger.composer.system.Transaction')) { + parameters.fileWriter.writeLine(0, 'hide org.hyperledger.composer.system.Transaction'); } - if (model.includes("org.hyperledger.composer.system.Event")) { - parameters.fileWriter.writeLine(0, "hide org.hyperledger.composer.system.Event"); + if (model.includes('org.hyperledger.composer.system.Event')) { + parameters.fileWriter.writeLine(0, 'hide org.hyperledger.composer.system.Event'); } - if (model.includes("org.hyperledger.composer.system.Registry")) { - parameters.fileWriter.writeLine(0, "hide org.hyperledger.composer.system.Registry"); + if (model.includes('org.hyperledger.composer.system.Registry')) { + parameters.fileWriter.writeLine(0, 'hide org.hyperledger.composer.system.Registry'); } } - //write closing sequence + // write closing sequence parameters.fileWriter.writeLine(0, 'right footer DATE'); parameters.fileWriter.writeLine(0, '@enduml'); - //send diagram text to client - connection.sendRequest("openUML", writer.getBuffer(), originatingFileName); + // send diagram text to client + connection.sendRequest('openUML', writer.getBuffer(), originatingFileName); } /** @@ -248,38 +257,38 @@ function handleGenerateUml(diagramTitle: string, originatingFileName: string) { * @param {TextDocument} textDocument - ".cto", "permissions.acl" or ".qry" document from the client to validate */ function validateTextDocument(textDocument: TextDocument): void { - let langId = textDocument.languageId; //type of file we are processing - //note - this is the FULL document text as we can't do incremental yet! - let txt = textDocument.getText(); + const langId = textDocument.languageId; // type of file we are processing + // note - this is the FULL document text as we can't do incremental yet! + const txt = textDocument.getText(); - //only add files with data + // only add files with data if (txt != null && txt.length > 0) { - //different behaviour for each language type - if (langId == "composer-acl") { - //permissions.acl file + // different behaviour for each language type + if (langId === 'composer-acl') { + // permissions.acl file validateNewAclModelFile(textDocument); - } else if (langId == "composer-qry") { - //.qry file + } else if (langId === 'composer-qry') { + // .qry file validateNewQueryModelFile(textDocument); } else { - //raw composer .cto file + // raw composer .cto file validateCtoModelFile(textDocument); - //if we have an acl file we should revalidate it incase the model changes broke something + // if we have an acl file we should revalidate it incase the model changes broke something const aclFile = aclManager.getAclFile(); if (aclFile != null) { validateExistingAclModelFile(aclFile); } - //if we have a query file we should revalidate it incase the model changes broke something + // if we have a query file we should revalidate it incase the model changes broke something const queryFile = queryManager.getQueryFile(); if (queryFile != null) { validateExistingQueryModelFile(queryFile); } } } else { - //clear any errors on the empty document - sendDiagnosticSuccess(textDocument.uri); //all OK + // clear any errors on the empty document + sendDiagnosticSuccess(textDocument.uri); // all OK } } @@ -291,81 +300,81 @@ function validateTextDocument(textDocument: TextDocument): void { */ function validateCtoModelFile(textDocument: TextDocument): void { try { - //debug - //var allNS = modelManager.getNamespaces(); - //connection.console.log("SERVER DOC-LEN: " + textDocument.getText().length); //debug - //connection.console.log("SERVER ALL-NS: " + allNS.length); //debug - //allNS.forEach(ns => { - //connection.console.log("SERVER NS: " + ns); //debug - //}); - - //hack to workaround circularly dependent documents - let currentModels = []; + // debug + // var allNS = modelManager.getNamespaces(); + // connection.console.log("SERVER DOC-LEN: " + textDocument.getText().length); //debug + // connection.console.log("SERVER ALL-NS: " + allNS.length); //debug + // allNS.forEach(ns => { + // connection.console.log("SERVER NS: " + ns); //debug + // }); + + // hack to workaround circularly dependent documents + const currentModels = []; documents.all().forEach((doc: TextDocument) => { - if (doc.languageId == "composer") { - let modelContents = doc.getText(); //*.cto file - if (modelContents.length > 0) { - //cannot add 0 length documents + if (doc.languageId === 'composer') { + const modelContent = doc.getText(); // *.cto file + if (modelContent.length > 0) { + // cannot add 0 length documents try { - let model: any = new ModelFile(modelManager, modelContents, doc.uri); - model.lineCount = doc.lineCount; //store the count so future errors have access - if (!modelManager.getModelFile(model.getNamespace())) { - //only add if not existing and no error - currentModels.push(model); + const TheModel: any = new ModelFile(modelManager, modelContent, doc.uri); + TheModel.lineCount = doc.lineCount; // store the count so future errors have access + if (!modelManager.getModelFile(TheModel.getNamespace())) { + // only add if not existing and no error + currentModels.push(TheModel); } } catch (err) { - //we had an error creating the model - output the error now - //connection.console.log("SERVER model err early: " + err.toString() + " " + doc.uri); //debug + // we had an error creating the model - output the error now + // connection.console.log("SERVER model err early: " + err.toString() + " " + doc.uri); //debug buildAndSendDiagnosticFromException(err, doc.lineCount, doc.uri); } } } }); - //connection.console.log("SERVER addModelFiles: " + currentModels.length); //debug + // connection.console.log("SERVER addModelFiles: " + currentModels.length); //debug if (currentModels.length > 0) { - //only add if we have files or it forces a validation too early! + // only add if we have files or it forces a validation too early! try { modelManager.addModelFiles(currentModels); } catch (err) { - //one of the files had an error validating, but the exception does not tell us which one so we must, - //ignore errors adding files here and wait until the user selects the file in error to report it. - //connection.console.log("SERVER model err adding: " + err.toString()); //debug + // one of the files had an error validating, but the exception does not tell us which one so we must, + // ignore errors adding files here and wait until the user selects the file in error to report it. + // connection.console.log("SERVER model err adding: " + err.toString()); //debug } } - let modelContents = textDocument.getText(); //*.cto file - //add or update, depending on existance. ModelFile and modelManager calls may throw an exception - let model: any = new ModelFile(modelManager, modelContents, textDocument.uri); - model.lineCount = textDocument.lineCount; //store the count so future errors have access - var existingModel = modelManager.getModelFile(model.getNamespace()); + const modelContents = textDocument.getText(); // *.cto file + // add or update, depending on existance. ModelFile and modelManager calls may throw an exception + const aModel: any = new ModelFile(modelManager, modelContents, textDocument.uri); + aModel.lineCount = textDocument.lineCount; // store the count so future errors have access + const existingModel = modelManager.getModelFile(aModel.getNamespace()); if (existingModel && existingModel.getName() === textDocument.uri) { - //update if we have a file that matches an existing namespace and filename - //connection.console.log("SERVER update model: " + model.getNamespace()); //debug - modelManager.updateModelFile(model); + // update if we have a file that matches an existing namespace and filename + // connection.console.log("SERVER update model: " + model.getNamespace()); //debug + modelManager.updateModelFile(aModel); } else { - //Composer does not allow two different files to belong to the same namespace, in which - //case addModelFile() will throw for us when we add the second instance. - //note, if we get here it will be because the file has an error or it would have been added above. - //connection.console.log("SERVER add model: " + model.getNamespace()); //debug - modelManager.addModelFile(model); + // Composer does not allow two different files to belong to the same namespace, in which + // case addModelFile() will throw for us when we add the second instance. + // note, if we get here it will be because the file has an error or it would have been added above. + // connection.console.log("SERVER add model: " + model.getNamespace()); //debug + modelManager.addModelFile(aModel); } - //finally check valiatation for cross validation for all additions and changes against other open models - modelManager.getModelFiles().forEach(model => { + // finally check valiatation for cross validation for all additions and changes against other open models + modelManager.getModelFiles().forEach( (model) => { try { model.validate(); - //clear any existing open errors against the file - sendDiagnosticSuccess(model.getName()); //the name is the uri + // clear any existing open errors against the file + sendDiagnosticSuccess(model.getName()); // the name is the uri } catch (err) { - //report any errors against the file + // report any errors against the file buildAndSendDiagnosticFromException(err, model.lineCount, model.getName()); } }); - sendDiagnosticSuccess(textDocument.uri); //all OK for current document - probably unnecessary to report it again... + sendDiagnosticSuccess(textDocument.uri); // all OK for current document - probably unnecessary to report it again... } catch (err) { - //connection.console.log("SERVER model err: " + err.toString() + " " + textDocument.uri); //debug + // connection.console.log("SERVER model err: " + err.toString() + " " + textDocument.uri); //debug buildAndSendDiagnosticFromException(err, textDocument.lineCount, textDocument.uri); } } @@ -377,11 +386,11 @@ function validateCtoModelFile(textDocument: TextDocument): void { */ function validateNewAclModelFile(textDocument: TextDocument): void { try { - let txt = textDocument.getText(); //permissions.acl file - let aclFile = aclManager.createAclFile(textDocument.uri, txt); - aclFile.lineCount = textDocument.lineCount; //store the count so future errors have access - aclManager.setAclFile(aclFile); //may throw an exception - sendDiagnosticSuccess(textDocument.uri); //all OK + const txt = textDocument.getText(); // permissions.acl file + const aclFile = aclManager.createAclFile(textDocument.uri, txt); + aclFile.lineCount = textDocument.lineCount; // store the count so future errors have access + aclManager.setAclFile(aclFile); // may throw an exception + sendDiagnosticSuccess(textDocument.uri); // all OK } catch (err) { buildAndSendDiagnosticFromException(err, textDocument.lineCount, textDocument.uri); } @@ -395,8 +404,8 @@ function validateNewAclModelFile(textDocument: TextDocument): void { */ function validateExistingAclModelFile(aclFile): void { try { - aclFile.validate(); //may throw an exception - sendDiagnosticSuccess(aclFile.getIdentifier()); //all OK + aclFile.validate(); // may throw an exception + sendDiagnosticSuccess(aclFile.getIdentifier()); // all OK } catch (err) { buildAndSendDiagnosticFromException(err, aclFile.lineCount, aclFile.getIdentifier()); } @@ -409,11 +418,11 @@ function validateExistingAclModelFile(aclFile): void { */ function validateNewQueryModelFile(textDocument: TextDocument): void { try { - let txt = textDocument.getText(); //.qry file - let queryFile = queryManager.createQueryFile(textDocument.uri, txt); - queryFile.lineCount = textDocument.lineCount; //store the count so future errors have access - queryManager.setQueryFile(queryFile); //may throw an exception - sendDiagnosticSuccess(textDocument.uri); //all OK + const txt = textDocument.getText(); // .qry file + const queryFile = queryManager.createQueryFile(textDocument.uri, txt); + queryFile.lineCount = textDocument.lineCount; // store the count so future errors have access + queryManager.setQueryFile(queryFile); // may throw an exception + sendDiagnosticSuccess(textDocument.uri); // all OK } catch (err) { buildAndSendDiagnosticFromException(err, textDocument.lineCount, textDocument.uri); } @@ -427,8 +436,8 @@ function validateNewQueryModelFile(textDocument: TextDocument): void { */ function validateExistingQueryModelFile(queryFile): void { try { - queryFile.validate(); //may throw an exception - sendDiagnosticSuccess(queryFile.getIdentifier()); //all OK + queryFile.validate(); // may throw an exception + sendDiagnosticSuccess(queryFile.getIdentifier()); // all OK } catch (err) { buildAndSendDiagnosticFromException(err, queryFile.lineCount, queryFile.getIdentifier()); } @@ -442,56 +451,56 @@ function validateExistingQueryModelFile(queryFile): void { * @private */ function buildAndSendDiagnosticFromException(err, lineCount: number, sourceURI: string): void { - let diagnostics: Diagnostic[] = []; - let curLine = 0; //vscode lines are 0 based. - let curColumn = 0; //vscode columns are 0 based - let endLine = lineCount; //default to highlighting to the end of document - let endColumn = Number.MAX_VALUE //default to highlighting to the end of the line - - //fill in the base description for the excption - let fullMsg = err.name + ": " - - //if it's a cto composer exception it will have a short message, but acl and qry ones do not - if (typeof err.getShortMessage === "function") { - //Short msg does not have any file and line info which is what we want + const diagnostics: Diagnostic[] = []; + let curLine = 0; // vscode lines are 0 based. + let curColumn = 0; // vscode columns are 0 based + let endLine = lineCount; // default to highlighting to the end of document + let endColumn = Number.MAX_VALUE; // default to highlighting to the end of the line + + // fill in the base description for the excption + let fullMsg = err.name + ': '; + + // if it's a cto composer exception it will have a short message, but acl and qry ones do not + if (typeof err.getShortMessage === 'function') { + // Short msg does not have any file and line info which is what we want fullMsg += err.getShortMessage(); } else { - //May have file and line info + // May have file and line info fullMsg += err.message; } let finalMsg = fullMsg; - //extract Line and Column info if we can, but not all errors have a real line and column - if (typeof err.getFileLocation === "function") { - //genuine composer exception - let location = err.getFileLocation(); - //we will take the default if we have no location + // extract Line and Column info if we can, but not all errors have a real line and column + if (typeof err.getFileLocation === 'function') { + // genuine composer exception + const location = err.getFileLocation(); + // we will take the default if we have no location if (location) { - curLine = location.start.line - 1; //Composer errors are 1 based + curLine = location.start.line - 1; // Composer errors are 1 based endLine = location.end.line - 1; - curColumn = location.start.column - 1; //Composer errors are 1 based + curColumn = location.start.column - 1; // Composer errors are 1 based endColumn = location.end.column - 1; } } else { - //possible composer exception - let index = fullMsg.lastIndexOf(". Line "); - if (index != -1) { - //manually pull out what we can. + // possible composer exception + let index = fullMsg.lastIndexOf('. Line '); + if (index !== -1) { + // manually pull out what we can. finalMsg = fullMsg.substr(0, index + 1); - let current = fullMsg.substr(index + 7); //step over ". Line " - curLine = parseInt(current, 10) - 1; //Composer errors are 1 based - if (isNaN(curLine) || curLine < 0) { curLine = 0; } //sanity check - endLine = curLine; //in the normal case only highlight the current line - index = current.lastIndexOf(" column "); - current = current.substr(index + 8); //step over " column " - curColumn = parseInt(current, 10) - 1; //Composer errors are 1 based - if (isNaN(curColumn) || curColumn < 0) { curColumn = 0; } //sanity check - endColumn = curColumn; //set to the same to highlight the current word + let current = fullMsg.substr(index + 7); // step over ". Line " + curLine = parseInt(current, 10) - 1; // Composer errors are 1 based + if (isNaN(curLine) || curLine < 0) { curLine = 0; } // sanity check + endLine = curLine; // in the normal case only highlight the current line + index = current.lastIndexOf(' column '); + current = current.substr(index + 8); // step over " column " + curColumn = parseInt(current, 10) - 1; // Composer errors are 1 based + if (isNaN(curColumn) || curColumn < 0) { curColumn = 0; } // sanity check + endColumn = curColumn; // set to the same to highlight the current word } } - //build the message to send back to the client + // build the message to send back to the client diagnostics.push({ severity: DiagnosticSeverity.Error, range: { @@ -503,7 +512,6 @@ function buildAndSendDiagnosticFromException(err, lineCount: number, sourceURI: source: 'Composer' }); - // Send the computed diagnostics to VSCode. This must always be sent because: // 1: If there has been an exception, this will report the details (this case). // 2: If there has NOT been an exception, this will clear any previous exception details. @@ -517,7 +525,7 @@ function buildAndSendDiagnosticFromException(err, lineCount: number, sourceURI: * @private */ function sendDiagnosticSuccess(sourceURI: string): void { - let diagnostics: Diagnostic[] = []; + const diagnostics: Diagnostic[] = []; // Send the computed diagnostics to VSCode. This must always be sent because: // 1: If there has been an exception, this will report the details. // 2: If there has NOT been an exception, this will clear any previous exception details (this case). @@ -529,77 +537,76 @@ function sendDiagnosticSuccess(sourceURI: string): void { * @param {TextDocument} textDocument - ".cto", "permissions.acl" or ".qry" document from the client to close */ function closeTextDocument(textDocument: TextDocument): void { - let langId = textDocument.languageId; //type of file we are processing - //note - this is the FULL document text as we can't do incremental yet! - let txt = textDocument.getText(); + const langId = textDocument.languageId; // type of file we are processing + // note - this is the FULL document text as we can't do incremental yet! + const txt = textDocument.getText(); - //only care about files with data + // only care about files with data if (txt != null && txt.length > 0) { - //different behaviour for each language type - if (langId == "composer-acl") { - //permissions.acl file - //TODO - close acl file - } else if (langId == "composer-qry") { - //.qry file - //TODO - close query file + // different behaviour for each language type + if (langId === 'composer-acl') { + // permissions.acl file + // TODO - close acl file + } else if (langId === 'composer-qry') { + // .qry file + // TODO - close query file } else { - //raw composer .cto file + // raw composer .cto file try { - //It is possible the model file could have unparsable errors but will - //handle this case another day (requires map of open files) - let model: any = new ModelFile(modelManager, txt, textDocument.uri); - var existingModel = modelManager.getModelFile(model.getNamespace()); + // It is possible the model file could have unparsable errors but will + // handle this case another day (requires map of open files) + const model: any = new ModelFile(modelManager, txt, textDocument.uri); + const existingModel = modelManager.getModelFile(model.getNamespace()); if (existingModel && existingModel.getName() === textDocument.uri) { - //only delete if we match on namespace and name + // only delete if we match on namespace and name modelManager.deleteModelFile(model.getNamespace()); } else { - //clear any existing errors on the closed document, otherwise they are ophaned - sendDiagnosticSuccess(textDocument.uri); //all OK + // clear any existing errors on the closed document, otherwise they are ophaned + sendDiagnosticSuccess(textDocument.uri); // all OK } - //finally check valiatation for cross validation against other open models - modelManager.getModelFiles().forEach(currrentModel => { + // finally check valiatation for cross validation against other open models + modelManager.getModelFiles().forEach((currrentModel) => { try { currrentModel.validate(); - //clear any existing open errors against the file - sendDiagnosticSuccess(currrentModel.getName()); //the name is the uri + // clear any existing open errors against the file + sendDiagnosticSuccess(currrentModel.getName()); // the name is the uri } catch (err) { - //report any errors against the still open file + // report any errors against the still open file buildAndSendDiagnosticFromException(err, currrentModel.lineCount, currrentModel.getName()); } }); } catch (err) { - //ignore errors as we are closing the file. - //connection.console.log("SERVER close err: " + err.toString() + " " + textDocument.uri); //debug + // ignore errors as we are closing the file. + // connection.console.log("SERVER close err: " + err.toString() + " " + textDocument.uri); //debug } - //if we have an acl file we should revalidate it incase the model changes broke something + // if we have an acl file we should revalidate it incase the model changes broke something const aclFile = aclManager.getAclFile(); if (aclFile != null) { validateExistingAclModelFile(aclFile); } - //if we have a query file we should revalidate it incase the model changes broke something + // if we have a query file we should revalidate it incase the model changes broke something const queryFile = queryManager.getQueryFile(); if (queryFile != null) { validateExistingQueryModelFile(queryFile); } } } else { - //clear any errors on the empty document - sendDiagnosticSuccess(textDocument.uri); //all OK + // clear any errors on the empty document + sendDiagnosticSuccess(textDocument.uri); // all OK } } connection.onDidChangeWatchedFiles((change) => { // Monitored files have change in VSCode - //connection.console.log('We received a file change event'); + // connection.console.log('We received a file change event'); }); - // This handler provides the initial list of the completion items. connection.onCompletion((textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => { - // The pass parameter contains the position of the text document in + // The pass parameter contains the position of the text document in // which code complete got requested. For the example we ignore this // info and always provide the same completion items. return [ @@ -623,7 +630,7 @@ connection.onCompletion((textDocumentPosition: TextDocumentPositionParams): Comp kind: CompletionItemKind.Text, data: 4 } - ] + ]; }); // This handler resolve additional information for the item selected in @@ -631,16 +638,16 @@ connection.onCompletion((textDocumentPosition: TextDocumentPositionParams): Comp connection.onCompletionResolve((item: CompletionItem): CompletionItem => { if (item.data === 1) { item.detail = 'asset details', - item.documentation = 'Add an asset.' + item.documentation = 'Add an asset.'; } else if (item.data === 2) { item.detail = 'participant details', - item.documentation = 'Add an participant' + item.documentation = 'Add an participant'; } else if (item.data === 3) { item.detail = 'transaction details', - item.documentation = 'Add an transaction' + item.documentation = 'Add an transaction'; } else if (item.data === 4) { item.detail = 'enum details', - item.documentation = 'Add an enum' + item.documentation = 'Add an enum'; } return item; }); @@ -669,4 +676,4 @@ connection.onDidCloseTextDocument((params) => { // Listen on the connection connection.listen(); -// handleGenerateUml; \ No newline at end of file +// handleGenerateUml; diff --git a/server/test/server.spec.ts b/server/test/server.spec.ts index 468e64b..9166060 100644 --- a/server/test/server.spec.ts +++ b/server/test/server.spec.ts @@ -1,27 +1,28 @@ -// -// Note: This example test is leveraging the Mocha test framework. -// Please refer to their documentation on https://mochajs.org/ for help. -// -// The module 'assert' provides assertion methods from node +/* + * 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. + */ import * as assert from 'assert'; - -// You can import and use all API from the 'vscode' module -// as well as import your extension to test it import * as path from 'path'; import { error } from 'util'; import * as server from '../src/server'; +import { DocumentSymbolRequest } from 'vscode-languageserver/lib/main'; +import { dirname } from 'path'; // Defines a Mocha test suite to group tests of similar kind together -describe("Server Tests", () => { - - - // test validation cto - it("validateCtoModelFile", () => { - - let originatingFileName: string = "./test/data/test.cto"; - console.log('is server called?'); - // server.validateCtoModelFile( originatingFileName); - - }); -}); \ No newline at end of file +describe('Language Server Tests', () => { + it('Dummy test', () => { + assert.equal(-1, [1, 2, 3].indexOf(5)); + assert.equal(-1, [1, 2, 3].indexOf(0)); + }); +}); diff --git a/server/tslint.json b/server/tslint.json new file mode 100644 index 0000000..46add27 --- /dev/null +++ b/server/tslint.json @@ -0,0 +1,70 @@ +{ + "extends": [ + "tslint:recommended" + ], + "rules": { + "trailing-comma": [ + false, + { + "multiline": "always", + "singleline": "never" + } + ], + "interface-name": [ + false, + "always-prefix" + ], + "indent": [ + true, + "spaces" + ], + "no-shadowed-variable": [ + true, + { + "class": true, + "enum": true, + "function": true, + "interface": false, + "namespace": true, + "typeAlias": false, + "typeParameter": false + } + ], + "array-type": false, + "prefer-for-of": false, + "member-access": false, + "no-console": [ + true, + "time", + "timeEnd", + "trace" + ], + "forin": false, + "max-line-length": false, + "no-string-literal": false, + "no-use-before-declare": true, + "object-literal-sort-keys": false, + "object-literal-shorthand": false, + "ordered-imports": false, + "quotemark": [ + true, + "single", + "avoid-escape" + ], + "variable-name": [ + true, + "allow-leading-underscore", + "allow-pascal-case", + "ban-keywords", + "check-format" + ], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ] + } +}