added name checking functionality for functionAppName (#60)

* added name checking functionality for functionAppName

* added submodule back

* Added package-lock back and updated gitignore
This commit is contained in:
Danish Dua 2019-02-20 19:22:24 -08:00 коммит произвёл Sahil Tara
Родитель 3fe4ba8eec
Коммит ce17f90abd
8 изменённых файлов: 814 добавлений и 612 удалений

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

@ -337,6 +337,8 @@ ASALocalRun/
**/package-lock.json
**/*.vsix
!templates/**/package-lock.json
# Ignore OS generated files #
.DS_Store
.DS_Store?

0
build Executable file → Normal file
Просмотреть файл

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

@ -1,16 +1,21 @@
import { ValidationHelper, FileHelper } from './utils';
import { AzureAuth } from '../azure-auth/azureAuth';
import { ServiceClientCredentials } from 'ms-rest';
import * as WebsiteManagement from 'azure-arm-website';
import { FileError, DeploymentError, AuthorizationError } from '../errors';
import { FileError, DeploymentError, AuthorizationError, ConnectionError } from '../errors';
import { SubscriptionItem, ResourceGroupItem } from '../azure-auth/AzureAuth';
import { config } from './config';
/*
* Runtime for the deployment, can be either 'dotnet' or 'node'.
*/
export type Runtime = "dotnet" | "node";
/*
* User selections from the wizard
* Cast the user selection JSON object returned by the wizard to the interface as:
* var selections: FunctionSelections = <FunctionSelections>userSelections;
*
* functionAppName: Globally unique app name for your function. Use checkFunctionAppName to validate
* subscriptionId: Subscription Id for users selected subscription
* subscriptionItem: Subscription Item for user's selected subscription
* resourceGroupName: Name for the resource group under the subscription selected by use.
* If user creates a new subscription, send that resource group name in selections after resource group is created.
* location: Location for deployment, can be West US/East US/China North/East Asia etc.
@ -21,10 +26,10 @@ import { FileError, DeploymentError, AuthorizationError } from '../errors';
* No duplicates allowed!
*
*
* JSON format:
* Format:
* {
* functionAppName: "YOUR_FUNCTION_APP_NAME",
* subscriptionId: "YOUR_SUBSCRIPTION_ID",
* subscriptionItem: {label: , subscriptionId: , session: , subscription: },
* location: "West US",
* runtime: "node",
* resourceGroupName: "YOUR_RESOURCE_GROUP",
@ -33,10 +38,10 @@ import { FileError, DeploymentError, AuthorizationError } from '../errors';
*/
export interface FunctionSelections {
functionAppName: string;
subscriptionId: string;
resourceGroupName: string;
subscriptionItem: SubscriptionItem;
resourceGroupItem: ResourceGroupItem;
location: string;
runtime: string;
runtime: Runtime;
functionNames: string[];
}
@ -48,7 +53,7 @@ export namespace FunctionProvider {
* Throws AuthoriaztionError if authorization fails.
* Throws DeploymentError if deployment fails.
*
* @param selections The user selection JSON casted to FunctionSelections interface
* @param selections The user selection object (FunctionSelections)
*
* @param appPath The path to original app being created by Web Template Studio
* The function app folder would be added under tis
@ -57,14 +62,12 @@ export namespace FunctionProvider {
*/
export async function createFunctionApp(selections: FunctionSelections, appPath: string): Promise<void> {
selections.runtime = selections.runtime.toLowerCase();
ValidationHelper.validate(selections); // throws validation error on failure
try {
let credentials: ServiceClientCredentials = AzureAuth.getCredentials();
let credentials: ServiceClientCredentials = selections.subscriptionItem.session.credentials;
var webClient = new WebsiteManagement.WebSiteManagementClient(credentials, selections.subscriptionId);
var webClient = new WebsiteManagement.WebSiteManagementClient(credentials, selections.subscriptionItem.subscriptionId);
} catch (err) {
throw new AuthorizationError(err.message);
}
@ -77,7 +80,7 @@ export namespace FunctionProvider {
}
try {
await webClient.webApps.createOrUpdate(selections.resourceGroupName, selections.functionAppName, {
await webClient.webApps.createOrUpdate(selections.resourceGroupItem.name, selections.functionAppName, {
location: selections.location,
kind: "functionapp",
siteConfig: {
@ -85,7 +88,7 @@ export namespace FunctionProvider {
}
});
await webClient.webApps.updateApplicationSettings(selections.resourceGroupName, selections.functionAppName, {
await webClient.webApps.updateApplicationSettings(selections.resourceGroupItem.name, selections.functionAppName, {
kind: "functionapp",
properties: {
"FUNCTIONS_EXTENSION_VERSION": "~2",
@ -103,13 +106,30 @@ export namespace FunctionProvider {
* Check if a function app name is available
*
* @param appName function app name to check uniqueness/availability for
*
* @param subscriptionItem Subscription Item for user's selected subscription/random subscription for user
*
* @returns Promise<boolean> True if the app name is available, false if it isn't
* catch errors as required
*/
export async function checkFunctionAppName(appName: string) : Promise<boolean> {
// TODO
return true;
export async function checkFunctionAppName(appName: string, subscriptionItem: SubscriptionItem) : Promise<boolean | undefined> {
try {
let credentials: ServiceClientCredentials = subscriptionItem.session.credentials;
var webClient = new WebsiteManagement.WebSiteManagementClient(credentials, subscriptionItem.subscriptionId);
} catch (err) {
throw new AuthorizationError(err.message);
}
ValidationHelper.validateFunctionAppName(appName);
return await webClient.checkNameAvailability(appName + config.functionAppDomain, "Site", {isFqdn: true})
.then((res) => {
return res.nameAvailable;
})
.catch((err) => {
throw new ConnectionError(err.message);
});
}
}

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

@ -4,16 +4,21 @@ import * as path from 'path';
import { config } from '../config';
import * as appRoot from 'app-root-path';
import * as archiver from 'archiver';
import { Runtime } from '../functionProvider';
export namespace FileHelper {
export function initFunctionDirectory(basePath: string, appName: string, functionNames: string[], runtime: string): void {
export function initFunctionDirectory(basePath: string, appName: string, functionNames: string[], runtime: Runtime): void {
let funcAppPath: string = path.join(basePath, appName);
mkdir(funcAppPath);
for (let i = 0; i < functionNames.length; i++) {
if (runtime === "node") {
mkdirForNode(path.join(funcAppPath, functionNames[i]));
switch (runtime) {
case "node":
mkdirForNode(path.join(funcAppPath, functionNames[i]));
break;
case "dotnet":
throw new Error("Runtime not implement yet");
}
}

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

@ -6,7 +6,6 @@ export namespace ValidationHelper {
export function validate(selections: FunctionSelections): void {
validateFunctionNames(selections.functionNames);
validateFunctionAppName(selections.functionAppName);
validateRuntime(selections.runtime);
}
function validateFunctionNames(names: string[]): void {
@ -21,16 +20,10 @@ export namespace ValidationHelper {
}
}
function validateFunctionAppName(name: string): void {
export function validateFunctionAppName(name: string): void {
let regexp = /^[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+$/;
if (!regexp.test(name)) {
throw new ValidationError("Invalid name for function: " + name);
}
}
function validateRuntime(name: string): void {
if (name !== "node" && name !== "dotnet") {
throw new ValidationError("Runtime can only be one of 'node'/'dotnet'");
}
}
}

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

@ -2,6 +2,7 @@ import * as vscode from "vscode";
import { ReactPanel } from "./reactPanel";
import { FunctionProvider } from './azure-functions/functionProvider';
import { ValidationError, DeploymentError, AuthorizationError } from './errors';
import { AzureAuth, SubscriptionItem, ResourceGroupItem } from './azure-auth/AzureAuth';
export function activate(context: vscode.ExtensionContext) {
//Launch the client wizard assuming it has been built
@ -21,16 +22,19 @@ export function activate(context: vscode.ExtensionContext) {
// NOTE: These can only be used after launch is called!
// Deploy a function app
// replace these with actual values from your portal to test! Saves the function app folder to your Documents folder
// fill a function app name and location here! Saves the function app folder to your Documents folder
context.subscriptions.push(vscode.commands.registerCommand(
'webTemplateStudioExtension.createFunctionApp', () => {
'webTemplateStudioExtension.createFunctionApp', async () => {
let subscriptionItem: SubscriptionItem = await tempGetSubscription("GiV.Hackathon");
let resourceGroupItem: ResourceGroupItem = await tempGetResourceGroup("GIV_W19_WTS", "GiV.Hackathon");
FunctionProvider.createFunctionApp(
{
functionAppName: "YOUR_UNIQUE_FUNCTION_APP_NAME",
subscriptionId: "YOUR_SUBSCRIPTION_ID",
subscriptionItem: subscriptionItem,
location: "YOUR_LOCATION",
runtime: "YOUR_RUNTIME",
resourceGroupName: "YOUR_RESOURCE_GROUP",
runtime: "node",
resourceGroupItem: resourceGroupItem,
functionNames: ["function1", "function2", "function3"]
},
require('path').join(require("os").homedir(), 'Documents')
@ -48,19 +52,25 @@ export function activate(context: vscode.ExtensionContext) {
case AuthorizationError:
console.log(err);
break;
default:
console.log(err);
break;
}
});
}
));
// Check function name availability
// NOT IMPLEMENTED YET
// Check function name availability, asks user for function app name as input and returns availability as a toast
// only for testing
context.subscriptions.push(vscode.commands.registerCommand(
'webTemplateStudioExtension.checkFunctionAppName', (appName: string) => {
FunctionProvider.checkFunctionAppName(appName)
'webTemplateStudioExtension.checkFunctionAppName', async (appName: string) => {
if (!appName) {
await vscode.window.showInputBox().then(value => appName = value!);
}
FunctionProvider.checkFunctionAppName(appName, await tempGetSubscription('GiV.Hackathon'))
.then((result) => {
// result is either true or false
vscode.window.showInformationMessage("Function App Name: " + name + "\nAvailable: " + String(result));
vscode.window.showInformationMessage("Function App Name: " + appName + "\nAvailable: " + String(result));
})
.catch((err) => {
console.log(err);
@ -68,4 +78,36 @@ export function activate(context: vscode.ExtensionContext) {
});
}
));
}
}
// temporary function that returns the Subscription Item for given subscription name
async function tempGetSubscription(name: string) : Promise<SubscriptionItem> {
let subItem: SubscriptionItem = <SubscriptionItem>{};
await AzureAuth.getSubscriptions().then((items) => {
for (let item of items) {
if (item.label === name) {
subItem = item;
break;
}
}
});
return subItem;
}
// temporary function that returns the ResourceGroup Item for given resource and subscription name
async function tempGetResourceGroup(name: string, subscription: string) : Promise<ResourceGroupItem> {
let resItem: ResourceGroupItem = <ResourceGroupItem>{};
await AzureAuth.getResourceGroupItems(await tempGetSubscription(subscription)).then((items) => {
for (let item of items) {
if (item.name === name) {
resItem = item;
}
}
});
return resItem;
}

1280
templates/Web/Projects/HtmlDefault/package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу