Skip to content
This repository was archived by the owner on Apr 13, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/commands/deployment/onboard.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const testPopulatedVal = (
const configYaml: ConfigYaml = {
introspection: {
azure: {
key: Promise.resolve("key")
key: "key"
}
}
};
Expand Down
2 changes: 1 addition & 1 deletion src/commands/deployment/onboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export interface CommandOptions {
*/
export const populateValues = (opts: CommandOptions): CommandOptions => {
const config = Config();
const { azure } = config.introspection!;
const azure = config.introspection ? config.introspection.azure : undefined;

opts.storageAccountName =
opts.storageAccountName || azure?.account_name || undefined;
Expand Down
30 changes: 11 additions & 19 deletions src/commands/deployment/validate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ describe("Validate deployment configuration", () => {
introspection: {
azure: {
account_name: uuid(),
key: Promise.resolve(uuid()),
key: uuid(),
partition_key: uuid(),
table_name: uuid()
}
Expand Down Expand Up @@ -210,7 +210,7 @@ describe("test runSelfTest function", () => {
const config: ConfigYaml = {
introspection: {
azure: {
key: Promise.resolve(uuid()),
key: uuid(),
table_name: undefined
}
}
Expand All @@ -229,7 +229,7 @@ describe("test runSelfTest function", () => {
const config: ConfigYaml = {
introspection: {
azure: {
key: Promise.resolve(uuid()),
key: uuid(),
table_name: undefined
}
}
Expand All @@ -244,7 +244,7 @@ describe("test runSelfTest function", () => {
const config: ConfigYaml = {
introspection: {
azure: {
key: Promise.resolve(uuid()),
key: uuid(),
table_name: undefined
}
}
Expand Down Expand Up @@ -279,7 +279,7 @@ describe("Validate missing deployment.storage configuration", () => {
introspection: {
azure: {
account_name: undefined,
key: Promise.resolve(uuid())
key: uuid()
}
}
};
Expand All @@ -292,7 +292,7 @@ describe("Validate missing deployment.storage configuration", () => {
const config: ConfigYaml = {
introspection: {
azure: {
key: Promise.resolve(uuid()),
key: uuid(),
table_name: undefined
}
}
Expand All @@ -306,7 +306,7 @@ describe("Validate missing deployment.storage configuration", () => {
const config: ConfigYaml = {
introspection: {
azure: {
key: Promise.resolve(uuid()),
key: uuid(),
partition_key: undefined
}
}
Expand All @@ -319,9 +319,7 @@ describe("Validate missing deployment.storage configuration", () => {
test("missing deployment.storage.key configuration", async () => {
const config: ConfigYaml = {
introspection: {
azure: {
key: Promise.resolve(undefined)
}
azure: {}
}
};
await expect(isValidConfig(config)).rejects.toThrow();
Expand All @@ -332,9 +330,7 @@ describe("Validate missing deployment.pipeline configuration", () => {
test("missing deployment.pipeline configuration", async () => {
const config: ConfigYaml = {
introspection: {
azure: {
key: Promise.resolve(undefined)
}
azure: {}
}
};
await expect(isValidConfig(config)).rejects.toThrow();
Expand All @@ -348,9 +344,7 @@ describe("Validate missing deployment.pipeline configuration", () => {
org: undefined
},
introspection: {
azure: {
key: Promise.resolve(undefined)
}
azure: {}
}
};
await expect(isValidConfig(config)).rejects.toThrow();
Expand All @@ -365,9 +359,7 @@ describe("Validate missing deployment.pipeline configuration", () => {
project: undefined
},
introspection: {
azure: {
key: Promise.resolve(undefined)
}
azure: {}
}
};
await expect(isValidConfig(config)).rejects.toThrow();
Expand Down
6 changes: 6 additions & 0 deletions src/commands/init.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ shall be no default values. These are the questions
1. Organization Name of Azure dev-op account
2. Project Name of Azure dev-op account
3. Personal Access Token (guides)
4. Would like to have introspection configuration setup? If yes
1. Storage Account Name
1. Storage Table Name
1. Storage Partition Key
1. Storage Access Key
1. Key Vault Name (optional)

This tool shall verify these values by making an API call to Azure dev-op. They
shall be written to `config.yaml` regardless the verification is successful or
Expand Down
53 changes: 51 additions & 2 deletions src/commands/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
execute,
getConfig,
handleInteractiveMode,
handleIntrospectionInteractive,
prompt,
validatePersonalAccessToken
} from "./init";
Expand Down Expand Up @@ -161,6 +162,9 @@ describe("test validatePersonalAccessToken function", () => {
expect(result).toBe(false);
done();
});
it("negative test, no values in parameter", async () => {
await expect(validatePersonalAccessToken({})).rejects.toThrow();
});
});

const testHandleInteractiveModeFunc = async (
Expand All @@ -171,19 +175,24 @@ const testHandleInteractiveModeFunc = async (
access_token: "",
org: "",
project: ""
},
introspection: {
azure: {}
}
});
jest.spyOn(init, "prompt").mockResolvedValueOnce({
azdo_org_name: "org_name",
azdo_pat: "pat",
azdo_project_name: "project"
azdo_project_name: "project",
toSetupIntrospectionConfig: true
});
jest
.spyOn(init, "validatePersonalAccessToken")
.mockReturnValueOnce(Promise.resolve(verified));
const tmpFile = path.join(createTempDir(), "config.yaml");

jest.spyOn(config, "defaultConfigFile").mockReturnValueOnce(tmpFile);
jest.spyOn(init, "handleIntrospectionInteractive").mockResolvedValueOnce();

await handleInteractiveMode();
const content = fs.readFileSync(tmpFile, "utf8");
Expand All @@ -209,11 +218,51 @@ describe("test prompt function", () => {
const answers = {
azdo_org_name: "org",
azdo_pat: "pat",
azdo_project_name: "project"
azdo_project_name: "project",
toSetupIntrospectionConfig: true
};
jest.spyOn(inquirer, "prompt").mockResolvedValueOnce(answers);
const ans = await prompt({});
expect(ans).toStrictEqual(answers);
done();
});
});

const testHandleIntrospectionInteractive = async (
withIntrospection = false,
withKeyVault = false
): Promise<void> => {
const config: ConfigYaml = {};
if (!withIntrospection) {
config["introspection"] = {
azure: {}
};
}
jest.spyOn(inquirer, "prompt").mockResolvedValueOnce({
azdo_storage_account_name: "storagetest",
azdo_storage_table_name: "storagetabletest",
azdo_storage_partition_key: "test1234key",
azdo_storage_access_key: "accessKey",
azdo_storage_key_vault_name: withKeyVault ? "keyvault" : ""
});
await handleIntrospectionInteractive(config);
expect(config.introspection?.azure?.account_name).toBe("storagetest");
expect(config.introspection?.azure?.table_name).toBe("storagetabletest");
expect(config.introspection?.azure?.partition_key).toBe("test1234key");
expect(config.introspection?.azure?.key).toBe("accessKey");

if (withKeyVault) {
expect(config.key_vault_name).toBe("keyvault");
} else {
expect(config.key_vault_name).toBeUndefined();
}
};

describe("test handleIntrospectionInteractive function", () => {
it("positive test", async () => {
await testHandleIntrospectionInteractive(false);
await testHandleIntrospectionInteractive(true);
await testHandleIntrospectionInteractive(false, true);
await testHandleIntrospectionInteractive(true, true);
});
});
92 changes: 60 additions & 32 deletions src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,9 @@ import {
saveConfiguration
} from "../config";
import { build as buildCmd, exit as exitCmd } from "../lib/commandBuilder";
import * as promptBuilder from "../lib/promptBuilder";
import { deepClone } from "../lib/util";
import {
hasValue,
validateAccessToken,
validateOrgName,
validateProjectName
} from "../lib/validator";
import { hasValue } from "../lib/validator";
import { logger } from "../logger";
import { ConfigYaml } from "../types";
import decorator from "./init.decorator.json";
Expand All @@ -31,6 +27,7 @@ interface Answer {
azdo_org_name: string;
azdo_project_name: string;
azdo_pat: string;
toSetupIntrospectionConfig: boolean;
}

/**
Expand All @@ -52,34 +49,17 @@ export const handleFileConfig = (file: string): void => {
*/
export const prompt = async (curConfig: ConfigYaml): Promise<Answer> => {
const questions = [
{
default: curConfig.azure_devops?.org || undefined,
message: "Enter organization name\n",
name: "azdo_org_name",
type: "input",
validate: validateOrgName
},
{
default: curConfig.azure_devops?.project || undefined,
message: "Enter project name\n",
name: "azdo_project_name",
type: "input",
validate: validateProjectName
},
{
default: curConfig.azure_devops?.access_token || undefined,
mask: "*",
message: "Enter your AzDO personal access token\n",
name: "azdo_pat",
type: "password",
validate: validateAccessToken
}
promptBuilder.azureOrgName(curConfig.azure_devops?.org),
promptBuilder.azureProjectName(curConfig.azure_devops?.project),
promptBuilder.azureAccessToken(curConfig.azure_devops?.access_token),
promptBuilder.askToSetupIntrospectionConfig(false)
];
const answers = await inquirer.prompt(questions);
return {
azdo_org_name: answers.azdo_org_name as string,
azdo_pat: answers.azdo_pat as string,
azdo_project_name: answers.azdo_project_name as string
azdo_project_name: answers.azdo_project_name as string,
toSetupIntrospectionConfig: answers.toSetupIntrospectionConfig
};
};

Expand All @@ -92,7 +72,7 @@ export const getConfig = (): ConfigYaml => {
loadConfiguration();
return Config();
} catch (_) {
// current config is not found.
logger.info("current config is not found.");
return {
azure_devops: {
access_token: "",
Expand Down Expand Up @@ -134,20 +114,68 @@ export const validatePersonalAccessToken = async (
}
};

export const isIntrospectionAzureDefined = (curConfig: ConfigYaml): boolean => {
if (!curConfig.introspection) {
return false;
}
const intro = curConfig.introspection;
return intro.azure !== undefined;
};

export const handleIntrospectionInteractive = async (
curConfig: ConfigYaml
): Promise<void> => {
if (!isIntrospectionAzureDefined(curConfig)) {
curConfig.introspection = {
azure: {}
};
}

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const azure = curConfig.introspection!.azure!;

const ans = await inquirer.prompt([
promptBuilder.azureStorageAccountName(azure.account_name),
promptBuilder.azureStorageTableName(azure.table_name),
promptBuilder.azureStoragePartitionKey(azure.partition_key),
promptBuilder.azureStorageAccessKey(azure.key),
promptBuilder.azureKeyVaultName(curConfig.key_vault_name)
]);
azure["account_name"] = ans.azdo_storage_account_name;
azure["table_name"] = ans.azdo_storage_table_name;
azure["partition_key"] = ans.azdo_storage_partition_key;
azure.key = ans.azdo_storage_access_key;

const keyVaultName = ans.azdo_storage_key_vault_name.trim();
if (keyVaultName) {
curConfig["key_vault_name"] = keyVaultName;
} else {
delete curConfig["key_vault_name"];
}
};

/**
* Handles the interactive mode of the command.
*/
export const handleInteractiveMode = async (): Promise<void> => {
const curConfig = deepClone(getConfig());
const conf = getConfig();
if (conf.introspection && conf.introspection.azure) {
delete conf.introspection.azure.key;
}
const curConfig = deepClone(conf);
const answer = await prompt(curConfig);

curConfig["azure_devops"] = curConfig.azure_devops || {};

curConfig.azure_devops.org = answer.azdo_org_name;
curConfig.azure_devops.project = answer.azdo_project_name;
curConfig.azure_devops["access_token"] = answer.azdo_pat;

if (answer.toSetupIntrospectionConfig) {
await handleIntrospectionInteractive(curConfig);
}

const data = yaml.safeDump(curConfig);

fs.writeFileSync(defaultConfigFile(), data);
logger.info("Successfully constructed SPK configuration file.");
const ok = await validatePersonalAccessToken(curConfig.azure_devops);
Expand Down
Loading