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:
Родитель
3fe4ba8eec
Коммит
ce17f90abd
|
@ -337,6 +337,8 @@ ASALocalRun/
|
|||
**/package-lock.json
|
||||
**/*.vsix
|
||||
|
||||
!templates/**/package-lock.json
|
||||
|
||||
# Ignore OS generated files #
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче