Commiting all latest code from

https://github.com/Azure/azure-sdk-for-node and making this to be the
new home for this extension
This commit is contained in:
Balaji Krishnan 2017-02-10 16:13:15 -08:00
Родитель 01757c3726
Коммит 39130f577b
21 изменённых файлов: 928 добавлений и 1 удалений

23
.eslintrc.json Normal file
Просмотреть файл

@ -0,0 +1,23 @@
{
"env": {
"browser": false,
"commonjs": true,
"es6": true,
"node": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"rules": {
"no-const-assign": "warn",
"no-this-before-super": "warn",
"no-undef": "warn",
"no-unreachable": "warn",
"no-unused-vars": "warn",
"constructor-super": "warn",
"valid-typeof": "warn"
}
}

1
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
node_modules

7
.vscode/extensions.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,7 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the extensions.json format
"recommendations": [
"dbaeumer.vscode-eslint"
]
}

22
.vscode/launch.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,22 @@
// A launch configuration that launches the extension inside a new window
{
"version": "0.1.0",
"configurations": [
{
"name": "Launch Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceRoot}" ],
"stopOnEntry": false
},
{
"name": "Launch Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/test" ],
"stopOnEntry": false
}
]
}

4
.vscode/settings.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,4 @@
// Place your settings in this file to overwrite default and user settings.
{
"typescript.tsdk": "./node_modules/typescript/lib" // we want to use the TS server from our node_modules folder to control its version
}

7
.vscodeignore Normal file
Просмотреть файл

@ -0,0 +1,7 @@
.vscode/**
.vscode-test/**
test/**
.gitignore
jsconfig.json
vsc-extension-quickstart.md
.eslintrc.json

Просмотреть файл

@ -1,3 +1,44 @@
# Contributing # Azure Node Essentials
This extension provides tools for NodeJs developers working with Azure SDKs.
## Feature List
1. Project and file scaffolding
* `yo azure-node` to create
* Javascript or Typescript project with package.json set up to target Azure SDKs
* Empty .js or .ts files
* pre populated .tsconfig or .jsconfig files
* create a service principal
1. Snippets for some common operations such as authentication, creating a service principal.
* `loginInt` : generate code for interactive login
* `loginPwd` : generate code for logging in with username and password
* `loginSp` : generate code for logging in with a service principal
* `spCreate` : generate code to create a service principal
1. Code generation scenarios
* command `Azure-Node: Generate code for template deployment` : generate code for template deployment
### Sample workflow
1. Open VS code with an empty workspace (empty folder)
1. Bring up VS code command palette, invoke `yo`
1. Choose `azure-node` generator and invoke it.
1. Choose `* app` to invoke the main generator (the sub generators for files are listed at the root level)
1. Choose a Javascript project and proceed.
1. This should initialize your project and install npm dependencies
1. Meanwhile, open the folder in VSCode and navigate to index.js
1. Notice that package.json has been set up and index.js has boiler plate code for authentication.
1. Place caret on the line after `// TODO: Write your application logic here.`
1. From VS Code's command palette, invoke `>Azure-Node: Generate code for template deployment`
1. The extension generates code for template deployment in the file currently active in the vscode editor and adds required dependencies to package.json.
## Dependencies
The following package/extension dependencies are auto installed when you install this extension.
1. [vscode yo](https://marketplace.visualstudio.com/items?itemName=samverschueren.yo)
1. [generator-azure-node](https://github.com/Azure/generator-azure-node)
## Contributing
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

19
changelog.md Normal file
Просмотреть файл

@ -0,0 +1,19 @@
# Change Log for Azure Node Essentials
## 0.2.3 [2017-02-08]
1. Bug fixes for Mac
1. updates to readme
1. check for generator package's version and upgrade if not latest
## 0.2.0 - 0.2.2 [2017-02-07]
1. No product change, updates to readme and other metadata about the project.
## 0.1.0 [2017-02-07]
1. Project and file scaffolding
1. Snippets for some common operations such as authentication, creating a service principal.
1. Code generation
* generate code for template deployment

12
jsconfig.json Normal file
Просмотреть файл

@ -0,0 +1,12 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"lib": [
"es6"
]
},
"exclude": [
"node_modules"
]
}

68
package.json Normal file
Просмотреть файл

@ -0,0 +1,68 @@
{
"name": "azurenodeessentials",
"displayName": "Azure Node Essentials",
"description": "Azure Node SDK Essentials for VS Code",
"version": "0.2.3",
"publisher": "azuresdkteam",
"engines": {
"vscode": "^1.8.0"
},
"repository": {
"type": "git",
"url": "https://github.com/Azure/azure-node-essentials"
},
"keywords": [
"azure",
"cloud",
"azure-sdk"
],
"icon": "resources/azure-icon.png",
"galleryBanner": {
"color": "#00abec",
"theme": "light"
},
"categories": [
"Other",
"Snippets"
],
"activationEvents": [
"*"
],
"main": "./src/extension",
"contributes": {
"snippets": [
{
"language": "javascript",
"path": "./src/snippets/snippets.json"
},
{
"language": "typescript",
"path": "./src/snippets/snippets.json"
}
],
"commands": [
{
"command": "Azure-Node.template-deploy",
"title": "Generate code for template deployment",
"category": "Azure-Node"
}
]
},
"scripts": {},
"dependencies": {
"npm": "*",
"escodegen": "^1.8.1",
"esprima": "^3.1.3"
},
"devDependencies": {
"typescript": "^2.0.3",
"vscode": "^1.0.0",
"mocha": "^2.3.3",
"eslint": "^3.6.0",
"@types/node": "^6.0.40",
"@types/mocha": "^2.2.32"
},
"extensionDependencies": [
"samverschueren.yo"
]
}

Двоичные данные
resources/azure-icon.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.2 KiB

108
src/codegen/codegen.js Normal file
Просмотреть файл

@ -0,0 +1,108 @@
var esprima = require('esprima');
var escodegen = require('escodegen');
/**
* Generates require('') statements to be inserted.
*
* This scans through the current document and determines the minimal non-duplicated list
* of imports we need to generate.
*
* @param {any} document the current file
* @returns a Set of require statements to be inserted.
*/
exports.generateRequireStatements = function generateRequireStatements(document, requiredModules) {
var foundImportsGroup = false;
var existingModules = new Set();
var requireStatementMatcher = /var\s*\w+\s+=\s+require\(\'([a-zA-Z0-9_-]*)\'\);*\s*/;
var insertionLine = 0;
// gather modules that are already imported in the document
for (var index = 0; index < document.lineCount; index++) {
var line = document.lineAt(index);
var matches = line.text.match(requireStatementMatcher);
if (matches) {
if (!foundImportsGroup) {
foundImportsGroup = true;
}
// as long as it matches the require statement, collect the list of imported modules.
existingModules.add(matches[1]);
} else if (foundImportsGroup) {
// we've already discovered a require statement previously and this new line no longer matches the pattern.
// we've gone past the require statement group. Do not loop through entire source text.
// record this line number, as we'll insert our require statements here. then, bail out of reading the source document.
insertionLine = index;
break;
}
}
// difference between existing modules and ones we require, are the ones we need to insert.
var modulesToInsert = requiredModules.filter(x => !existingModules.has(x));
// generate code for importing modules.
var requireStatements = new Set();
for (var module of modulesToInsert) {
var name = this.getNameAssignmentForModule(module);
var statement = this.generateRequireStatement(name, module)
requireStatements.add(statement);
}
var result = {
line: insertionLine,
code: requireStatements
};
return result;
};
/**
* Generates a require('') statement with given information.
*
* This will not perform any additional checks. If you need to generate only
* if module is not already imported, use `generateRequireStatements`
*/
exports.generateRequireStatement = function generateRequireStatement(name, packageName) {
if (!name) {
name = packageName;
}
var text = `var ${name} = require('${packageName}');\r\n`;
return text;
};
// TODO: do this programmatically. Normalize 'name', strip dashes and other illegal chars. use pascal casing.
exports.getNameAssignmentForModule = function getNameAssignmentForModule(module) {
switch (module) {
case 'azure-arm-resource':
return 'ResourceManagement';
case 'ms-rest':
return 'msRest';
case 'ms-rest-azure':
return 'msRestAzure';
default:
return;
}
};
exports.generateNewLine = function generateNewLine() {
return this.generateCode('\r\n');
};
// parse the text blob and emit code from the AST.
exports.generateCode = function generateCode(text) {
var ast = esprima.parse(text, { raw: true, tokens: true, range: true, comment: true });
ast = escodegen.attachComments(ast, ast.comments, ast.tokens);
var codegenOptions = {
comment: true,
format: {
indent: {
style: ' '
},
preserveBlankLines: true,
},
sourceCode: text
};
var code = escodegen.generate(ast, codegenOptions);
return code;
};

Просмотреть файл

@ -0,0 +1,48 @@
var codegenerator = require('./codegen');
const deployTemplateFunctionName = 'deployTemplate';
// Generates NodeJs code for arm template deployment.
exports.deployTemplate = function deployTemplate() {
var text = `function ${deployTemplateFunctionName}(credentials, callback){\
// TODO: initialize these variables
var subscriptionId;\
var resourceGroupName;\
var deploymentName;\
var templateFilePath;\
var templateParametersFilePath;\
var template;\
var templateParameters;\
var parameters = {\
template: template,\
parameters: templateParameters,\
mode: \'Complete\'\
};\
\r\n
try {\
template = JSON.parse(fs.readFileSync(templateFilePath));\
templateParameters = JSON.parse(fs.readFileSync(templateParametersFilePath));\
} catch (error) {\
console.error('Encountered error parsing template file:', error);\
}\
\r\n
var resourceClient = new ResourceManagement.ResourceManagementClient(credentials, subscriptionId);\
resourceClient.deployments.createOrUpdate(resourceGroupName, deploymentName, parameters, callback);\
}`;
return codegenerator.generateCode(text);
};
exports.generateRequireStatements = function generateRequireStatements(document) {
const requiredModules = ['fs', 'azure-arm-resource', 'ms-rest', 'ms-rest-azure'];
return codegenerator.generateRequireStatements(document, requiredModules);
};
exports.deployTemplateCallSite = function deployTemplateCallSite() {
var text = `${deployTemplateFunctionName}(credentials, function(err, result){ });`;
return codegenerator.generateCode(text);
};
exports.getPackageDependencies = function getPackageDependencies() {
return ['azure-arm-resource', 'ms-rest', 'ms-rest-azure'];
}

95
src/codegen/jsoneditor.js Normal file
Просмотреть файл

@ -0,0 +1,95 @@
var fs = require('fs');
var npm = require('npm');
/**
* Removes byte order marker. This catches EF BB BF (the UTF-8 BOM)
* because the buffer-to-string conversion in `fs.readFile()`
* translates it to FEFF, the UTF-16 BOM.
*/
exports.stripBOM = function stripBOM(content) {
if (Buffer.isBuffer(content)) {
content = content.toString();
}
if (content.charCodeAt(0) === 0xFEFF || content.charCodeAt(0) === 0xFFFE) {
content = content.slice(1);
}
return content;
};
/**
* Provides a parsed JSON from the given local file path
*/
exports.parseJson = function parseJson(filePath) {
let result = null;
if (!filePath || (filePath && typeof filePath.valueOf() !== 'string')) {
let err = new Error('a local file path to package.json is required and must be of type string.');
return Promise.reject(err);
}
try {
result = JSON.parse(this.stripBOM(fs.readFileSync(filePath, 'utf8')));
return Promise.resolve(result);
} catch (err) {
return Promise.reject(err);
}
};
/**
* add given list of dependencies to package.json
*/
exports.addDependenciesIfRequired = function addDependenciesIfRequired(filePath, packages) {
this.parseJson(filePath).then(function (jsonObject) {
if (jsonObject) {
var existingPackages = new Set();
for (var item in jsonObject.dependencies) {
existingPackages.add(item);
}
var packagesToInsert = packages.filter(x => !existingPackages.has(x));
var promises = packagesToInsert.map((pkgName) => updateDependency(pkgName));
Promise.all(promises).then(function (entries) {
if (!jsonObject.dependencies) {
jsonObject.dependencies = {};
}
for (var item in entries) {
var kvp = entries[item].split(':');
jsonObject.dependencies[kvp[0]] = kvp[1];
}
fs.writeFileSync(filePath, JSON.stringify(jsonObject, null, ' '));
}).catch(function (err) {
return console.error(err);
});
}
});
};
/**
* Given a package name as string, pings npm about the package and
* returns a well formed package dependency entry that can be inserted in package.json
*/
function updateDependency(pkgName) {
var promise = new Promise(function (resolve, reject) {
npm.load(function (err) {
if (err) {
return reject(err);
}
npm.commands.view([pkgName, 'name', 'version'], true, function (err, info) {
if (err) {
return reject(err);
}
var json = JSON.parse(JSON.stringify(info));
var dependency = json[Object.keys(json)[0]];
var entry = `${dependency.name}:^${dependency.version}`;
return resolve(entry);
});
});
});
return promise;
};

Просмотреть файл

@ -0,0 +1,66 @@
var fs = require('fs');
var path = require('path');
var vscode = require('vscode');
var codegen = require('../codegen/codgen.template-deploy');
var jsonEditor = require('../codegen/jsoneditor');
exports.createCommand = function createCommand() {
vscode.commands.registerCommand('Azure-Node.template-deploy', function () {
if (!vscode.window.activeTextEditor) {
vscode.window.showInformationMessage(`please open a .js file in the editor and then use this code generator command.`);
return;
}
// update package.json
updatePackageJson();
// generate code in current document
return generateCodeInEditor();
});
};
function updatePackageJson() {
// TODO: search rootDir\package.json
var filePath = path.join(vscode.workspace.rootPath, 'package.json');
if (fs.existsSync(filePath)) {
var packages = codegen.getPackageDependencies();
jsonEditor.addDependenciesIfRequired(filePath, packages);
}
};
function generateCodeInEditor() {
// generate code to be inserted.
const document = vscode.window.activeTextEditor.document;
const lineCount = document.lineCount;
var importsAndLineNumber = codegen.generateRequireStatements(document);
var methodBody = codegen.deployTemplate();
var callsite = codegen.deployTemplateCallSite();
vscode.window.activeTextEditor.edit((builder) => {
// insert import statements.
// Insertion point is the line where import group ends.
if (importsAndLineNumber) {
var importPos = new vscode.Position(importsAndLineNumber.line, 0);
var imports = importsAndLineNumber.code;
for (var importStatement of imports) {
builder.insert(importPos, importStatement);
}
}
// insert code for template deployment.
const range = new vscode.Range(new vscode.Position(lineCount, 0), new vscode.Position(lineCount + 1, 0));
builder.replace(range, methodBody);
// fix callsite to invoke the function that was newly generated.
const currentPos = new vscode.Position(vscode.window.activeTextEditor.selection.active.line, 0);
builder.insert(currentPos, callsite);
});
// format the entire document.
// the code we inserted was generated as well-formatted but indenting is relative to the existing text
// in the document. Since we didn't examine existing text and are unaware of the indent depth where
// generated code will be inserted, we have to reformat the whole document. If this leads to performance issues, we'll revisit this logic.
return vscode.commands.executeCommand("editor.action.formatDocument");
};

107
src/extension.js Normal file
Просмотреть файл

@ -0,0 +1,107 @@
let vscode = require('vscode');
let path = require('path');
let fs = require('fs');
let utils = require('./utils');
// TODO: find a way to have the extension auto update the package dependency, instead of having an update to VSCode extension.
// TODO: try to optimize time spent in this method.
// instead of enumerating all the installed npm packages every time on activation. drop a file to this extension's install path
// after installing dependencies. after the first time, just check for the presence of that sentinel file and bail.
// that does not ensure the dependency will always exist though.
// TODO: check for package version updates and re-install if necessary.
// Called once to activate the extension.
// The only thing this extension needs to do is to ensure that a certain npm package is installed globally.
// Ideally this should be done during install time, but VSCode does not support install time tasks.
// So, we do this on activation. Ideally, this is a one time task.
function activate(context) {
// Register commands.
var commandFilesPath = path.join(context.extensionPath, 'src', 'commands');
fs.readdir(commandFilesPath, (err, files) => {
files.forEach((file) => {
context.subscriptions.push(
require('./commands/' + path.basename(file, '.js')).createCommand()
);
console.log(path.basename(file, '.js') + ' command added');
});
});
// Install dependencies.
ensureDependenciesAreInstalled();
}
function ensureDependenciesAreInstalled() {
// Download and install template generator package.
var extensionName = 'Azure-Node-Essentials';
var generatorPackageName = 'generator-azure-node';
var generatorPackageVersion = '0.1.0'; // TODO: query npm and obtain latest version. That would mean updating package would not require an extension update.
utils.isNodeInstalled().then(function (result) {
if (!result) {
vscode.window.showInformationMessage(`Please install NodeJS and then run ${extensionName} extension.`);
return;
}
// check the npm cache on this machine and determine if our dependency is present.
// if the dependency is present and is the latest version, there is nothing more to do.
// if it is not present or is not the latest, install it from npm.
utils.npmList().then(function (listOfPackages) {
var generatorPackage = listOfPackages.find(function (item) {
return item.startsWith(generatorPackageName);
});
var packageInfo = { present: false, needsUpgrade: false };
if (generatorPackage) {
packageInfo.present = true;
packageInfo.needsUpgrade = generatorPackage.split('@')[1] !== generatorPackageVersion;
}
return Promise.resolve(packageInfo);
}).then(function (packageInfo) {
if (!packageInfo.present || packageInfo.needsUpgrade) {
var options = {
global: true
};
var actionPerformed;
if (packageInfo.needsUpgrade) {
actionPerformed = 'upgrade';
} else {
actionPerformed = 'install';
// indicate to the user that the extension needs to perform some one-time startup tasks
vscode.window.showInformationMessage(`${extensionName} is installing dependencies...`);
}
var installTask = utils.npmInstall([generatorPackageName], options);
return installTask.then(
function onFulfilled(value) {
return { action: actionPerformed, status: value };
}
);
}
else {
var result = { action: 'pre-installed', status: true }; // (pre-installed, nothing to do.)
return Promise.resolve(result);
}
}).then(function (result) {
if (result && result.status === true) {
if (result.action === 'install') {
vscode.window.showInformationMessage(`${extensionName} successfully installed dependencies and is ready for use.`);
} else if (result.action === 'upgrade') {
vscode.window.setStatusBarMessage(`${extensionName} successfully upgraded dependencies in the background.`);
}
}
}).catch(function (err) {
vscode.window.showInformationMessage(`An error occurred while ${extensionName} was installing dependencies. ${err}`);
});
});
}
exports.activate = activate;
function deactivate() {
}
exports.deactivate = deactivate;

136
src/snippets/snippets.json Normal file
Просмотреть файл

@ -0,0 +1,136 @@
{
"Login Interactively": {
"prefix": "loginInt",
"body": "msRestAzure.interactiveLogin(function(${1:err}, ${2:credentials}) {\n\tif (err) {\n\t\tconsole.log(err);\n\t\treturn;\n\t}\n\t$0\n});",
"description": "Interactively login to Azure account"
},
"Login with Service Principal": {
"prefix": "loginSp",
"body": "msRestAzure.loginWithServicePrincipalSecret(${1:clientId}, ${2:secret}, ${3:domain}, function(${4:err}, ${5:credentials}) {\n\tif (err) {\n\t\tconsole.log(err);\n\t\treturn;\n\t}\n\t$0\n});",
"description": "Login with Service principal into Azure account"
},
"Login with Password": {
"prefix": "loginPwd",
"body": "msRestAzure.loginWithUsernamePassword(${1:username}, ${2:password}, function(${3:err}, ${4:credentials}) {\n\tif (err) {\n\t\tconsole.log(err);\n\t\treturn;\n\t}\n\t$0\n});",
"description": "Login with username and password into Azure account"
},
"Create Service Principal": {
"prefix": "spCreate",
"body": ["'use strict';",
"",
// Documentation text @0-indent
"// Steps:",
"// 1. Create AD application",
"// 2. Create SP on top of the AD application",
"// 3. Assigning the Contributor role to the SP",
"",
"function createServicePrincipal() {",
// imports @1-indent
"\tvar msrestazure = require('ms-rest-azure');",
"\tvar graph = require('azure-graph');",
"\tvar authorization = require('azure-arm-authorization');",
"\tvar util = require('util');",
"\tvar moment = require('moment');",
"",
// variable declarations @1-indent
"\t// TODO: Initialize these variables",
"\tvar ${0:tenantId;}",
"\tvar ${1:subscriptionId;}",
"\tvar ${2:passwordForSp;}",
"\tvar ${3:displayName;}",
"",
"\tvar homePage = 'http://' + displayName + ':8080';",
"\tvar identifierUris = [ homePage ];",
"\tvar roleId = 'b24988ac-6180-42a0-ab88-20f7382dd24c'; // contributor role",
"\tvar scope = '/subscriptions/' + subscriptionId;",
"\tvar roleDefinitionId = scope + '/providers/Microsoft.Authorization/roleDefinitions/' + roleId;",
"\tvar loginOptions = {",
"\t\tdomain: tenantId",
"\t};",
"",
"\tmsrestazure.interactiveLogin(loginOptions, function(err, creds) {",
"\t\tif (err) {",
"\t\t\tconsole.log('Error occured in interactive login: \\n' + util.inspect(err, { depth: null }));",
"\t\t\treturn;",
"\t\t}",
"",
// more variable declarations @2-indent
"\t\tvar options = {",
"\t\t\tdomain: tenantId,",
"\t\t\ttokenAudience: 'graph',",
"\t\t\tusername: creds.username,",
"\t\t\ttokenCache: creds.tokenCache,",
"\t\t\tenvironment: creds.environment",
"\t\t};",
"\t\tvar credsForGraph = new msrestazure.DeviceTokenCredentials(options);",
"\t\tvar graphClient = new graph(credsForGraph, tenantId);",
"\t\tvar startDate = new Date(Date.now());",
"\t\tvar endDate = new Date(startDate.toISOString());",
"\t\tvar m = moment(endDate);",
"\t\tm.add(1, 'years');",
"\t\tendDate = new Date(m.toISOString());",
"\t\tvar applicationCreateParameters = {",
"\t\t\tavailableToOtherTenants: false,",
"\t\t\tdisplayName: displayName,",
"\t\t\thomePage: homePage,",
"\t\t\tidentifierUris: identifierUris,",
"\t\t\tpasswordCredentials: [{",
"\t\t\t\tstartDate: startDate,",
"\t\t\t\tendDate: endDate,",
"\t\t\t\tkeyId: msrestazure.generateUuid(),",
"\t\t\t\tvalue: passwordForSp",
"\t\t\t}]",
"\t\t};",
"",
// 1. Create AD application @2-indent
"\t\tgraphClient.applications.create(applicationCreateParameters, function (err, application, req, res) {",
"\t\t\tif (err) {",
"\t\t\t\tconsole.log('Error occured while creating the application: \\n' + util.inspect(err, { depth: null }));",
"\t\t\t\treturn;",
"\t\t\t}",
"",
// 2. Create SP @3-indent
"\t\t\tvar servicePrincipalCreateParameters = {",
"\t\t\t\tappId: application.appId,",
"\t\t\t\taccountEnabled: true",
"\t\t\t};",
"",
"\t\t\tgraphClient.servicePrincipals.create(servicePrincipalCreateParameters, function (err, sp, req, res) {",
"\t\t\t\tif (err) {",
"\t\t\t\t\tconsole.log('Error occured while creating the servicePrincipal: \\n' + util.inspect(err, { depth: null }));",
"\t\t\t\t\treturn;",
"\t\t\t\t}",
"",
// 3. SP role assignment @4-indent
"\t\t\t\tvar authorizationClient = new authorization(creds, subscriptionId, null);",
"\t\t\t\tvar assignmentGuid = msrestazure.generateUuid();",
"\t\t\t\tvar roleCreateParams = {",
"\t\t\t\t\tproperties: {",
"\t\t\t\t\t\tprincipalId: sp.objectId,",
"\t\t\t\t\t\troleDefinitionId: roleDefinitionId,",
"\t\t\t\t\t\tscope: scope",
"\t\t\t\t\t}",
"\t\t\t\t};",
"",
"\t\t\t\tauthorizationClient.roleAssignments.create(scope, assignmentGuid, roleCreateParams, function (err, roleAssignment, req, res) {",
"\t\t\t\t\tif (err) {",
"\t\t\t\t\t\tconsole.log('\\nError occured while creating the roleAssignment: \\n' + util.inspect(err, { depth: null }));",
"\t\t\t\t\t\treturn;",
"\t\t\t\t\t}",
"",
// Print SP information to console
"\t\t\t\t\tconsole.log('>>>>>>>>>>>\\nSuccessfully created the role assignment for the servicePrincipal.\\n');",
"\t\t\t\t\tconsole.log('>>>>>>>>>>>\\nIn future for login you will need the following info:');",
"\t\t\t\t\tconsole.log('ServicePrincipal Id (SPN): ' + sp.appId);",
"\t\t\t\t\tconsole.log('ServicePincipal Password: ' + passwordForSp);",
"\t\t\t\t\tconsole.log('Tenant Id for ServicePrincipal: ' + tenantId);",
"\t\t\t\t\tconsole.log('>>>>>>>>>>>\\n');",
"\t\t\t\t});", // close sp role assignment
"\t\t\t});", // close sp create
"\t\t});", // close AD application create
"\t});", // close interactive login
"}" // close function createServicePrincipal
],
"description": "Create a service principal"
}
}

84
src/utils.js Normal file
Просмотреть файл

@ -0,0 +1,84 @@
let exec = require('child_process').exec;
// checks if there exists a valid installation of NodeJs on this machine
exports.isNodeInstalled = function isNodeInstalled() {
var cmdString = "node -v";
return new Promise(function (resolve, reject) {
exec(cmdString, (error, stdout) => {
if (error) {
return reject(error);
}
if (stdout.startsWith('v')) {
return resolve(true);
}
return resolve(false);
});
});
};
// lists all globally installed npm packages.
exports.npmList = function npmList(path) {
var global = false;
if (!path) global = true;
var cmdString = "npm ls --depth=0 " + (global ? "-g " : " ");
return new Promise(function (resolve, reject) {
exec(cmdString, { cwd: path ? path : "/" }, (error, stdout) => {
if (error && !stdout) {
return reject(error);
}
var packages = [];
packages = stdout.split('\n');
packages = packages.filter(function (item) {
if (item.match(/^\+--.+/g) != null || item.match(/^├──.+/g) != null) {
return true;
}
if (item.match(/^`--.+/g) != null || item.match(/^└──.+/g) != null) {
return true;
}
return undefined;
});
packages = packages.map(function (item) {
// windows
if (item.match(/^\+--.+/g) != null) {
return item.replace(/^\+--\s/g, "");
}
if (item.match(/^`--.+/g) != null) {
return item.replace(/^`--\s/g, "");
}
// mac
if (item.match(/^├──.+/g) != null) {
return item.replace(/^├──\s/g, "");
}
if (item.match(/^└──.+/g) != null) {
return item.replace(/^└──\s/g, "");
}
})
resolve(packages);
});
});
};
// install given list of npm packages to the global location.
exports.npmInstall = function npmInstall(packages, opts) {
if (packages.length == 0 || !packages || !packages.length) { Promise.reject("No packages found"); }
if (typeof packages == "string") packages = [packages];
if (!opts) opts = {};
var cmdString = "npm install " + packages.join(" ") + " "
+ (opts.global ? " -g" : "")
+ (opts.save ? " --save" : "")
+ (opts.saveDev ? " --saveDev" : "");
return new Promise(function (resolve, reject) {
exec(cmdString, { cwd: opts.cwd ? opts.cwd : "/" }, (error) => {
if (error) {
reject(error);
} else {
resolve(true); // return success.
}
});
});
};

24
test/extension.test.js Normal file
Просмотреть файл

@ -0,0 +1,24 @@
/* global suite, test */
//
// 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
var assert = require('assert');
// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
var vscode = require('vscode');
var myExtension = require('../extension');
// Defines a Mocha test suite to group tests of similar kind together
suite("Extension Tests", function() {
// Defines a Mocha unit test
test("Something 1", function() {
assert.equal(-1, [1, 2, 3].indexOf(5));
assert.equal(-1, [1, 2, 3].indexOf(0));
});
});

22
test/index.js Normal file
Просмотреть файл

@ -0,0 +1,22 @@
//
// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
//
// This file is providing the test runner to use when running extension tests.
// By default the test runner in use is Mocha based.
//
// You can provide your own test runner if you want to override it by exporting
// a function run(testRoot: string, clb: (error:Error) => void) that the extension
// host can call to run the tests. The test runner is expected to use console.log
// to report the results back to the caller. When the tests are finished, return
// a possible error to the callback or null if none.
var testRunner = require('vscode/lib/testrunner');
// You can directly control Mocha options by uncommenting the following lines
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
testRunner.configure({
ui: 'tdd', // the TDD UI is being used in extension.test.js (suite, test, etc.)
useColors: true // colored output from test results
});
module.exports = testRunner;

Просмотреть файл

@ -0,0 +1,33 @@
# Welcome to your first VS Code Extension
## What's in the folder
* This folder contains all of the files necessary for your extension
* `package.json` - this is the manifest file in which you declare your extension and command.
The sample plugin registers a command and defines its title and command name. With this information
VS Code can show the command in the command palette. It doesnt yet need to load the plugin.
* `extension.js` - this is the main file where you will provide the implementation of your command.
The file exports one function, `activate`, which is called the very first time your extension is
activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
We pass the function containing the implementation of the command as the second parameter to
`registerCommand`.
## Get up and running straight away
* press `F5` to open a new window with your extension loaded
* run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`
* set breakpoints in your code inside extension.ts to debug your extension
* find output from your extension in the debug console
## Make changes
* you can relaunch the extension from the debug toolbar after changing code in `extension.js`
* you can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes
## Explore the API
* you can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts`
## Run tests
* open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Launch Tests`
* press `F5` to run the tests in a new window with your extension loaded
* see the output of the test result in the debug console
* make changes to `test/extension.test.js` or create new test files inside the `test` folder
* by convention, the test runner will only consider files matching the name pattern `**.test.js`
* you can create folders inside the `test` folder to structure your tests any way you want