From 9e4cf9b887917ad4f1dc00bcbd888ea4e3c63940 Mon Sep 17 00:00:00 2001 From: Matteo Boschi Date: Mon, 17 Jun 2019 15:53:56 +0200 Subject: [PATCH 01/10] fix test fix minor bugs --- src/commands/profiles/list.ts | 4 +-- src/utils/azure.ts | 18 +++++++++++++ test/commands/hello.test.ts | 22 ++++++++-------- test/commands/users/list.test.ts | 44 ++++++++++++++++++++------------ test/mocha.opts | 2 +- 5 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/commands/profiles/list.ts b/src/commands/profiles/list.ts index 7a9dd57..989f014 100644 --- a/src/commands/profiles/list.ts +++ b/src/commands/profiles/list.ts @@ -24,9 +24,9 @@ export default class ProfilesList extends Command { cli.action.start("Querying profiles..."); const client = new cosmos.CosmosClient({ endpoint, auth: { key } }); - const database = await client.database("agid-documentdb-test"); + const database = client.database("agid-documentdb-test"); const container = database.container("profiles"); - const response = await container.items.query( + const response = container.items.query( "SELECT c.fiscalCode, c._ts FROM c WHERE c.version = 0", { enableCrossPartitionQuery: true diff --git a/src/utils/azure.ts b/src/utils/azure.ts index 794e025..543efcf 100644 --- a/src/utils/azure.ts +++ b/src/utils/azure.ts @@ -42,3 +42,21 @@ export const getStorageConnection = async (name: string) => (await execa( `az storage account show-connection-string --name ${name} --output tsv` )).stdout; + +/** + * hasCosmosConnection checks if the host has az cli installed and resouceGroup and name + * are valid inputs for cosmosdb + */ +export const hasCosmosConnection = (resourceGroup: string, name: string) => { + try { + execa.sync( + `az cosmosdb show -g ${resourceGroup} -n ${name} --query documentEndpoint -o tsv` + ); + execa.sync( + `az cosmosdb list-keys -g ${resourceGroup} -n ${name} --query primaryReadonlyMasterKey -o tsv` + ); + return true; + } catch (e) { + return false; + } +}; diff --git a/test/commands/hello.test.ts b/test/commands/hello.test.ts index 651a421..2662e44 100644 --- a/test/commands/hello.test.ts +++ b/test/commands/hello.test.ts @@ -1,17 +1,17 @@ -import {expect, test} from '@oclif/test' +import { expect, test } from "@oclif/test"; -describe('hello', () => { +describe("hello", () => { test .stdout() - .command(['hello']) - .it('runs hello', ctx => { - expect(ctx.stdout).to.contain('hello world') - }) + .command(["hello"]) + .it("runs hello world", ctx => { + expect(ctx.stdout).to.contain("hello world"); + }); test .stdout() - .command(['hello', '--name', 'jeff']) - .it('runs hello --name jeff', ctx => { - expect(ctx.stdout).to.contain('hello jeff') - }) -}) + .command(["hello", "--name", "jeff"]) + .it("runs hello --name jeff", ctx => { + expect(ctx.stdout).to.contain("hello jeff"); + }); +}); diff --git a/test/commands/users/list.test.ts b/test/commands/users/list.test.ts index 380669e..be4fd6e 100644 --- a/test/commands/users/list.test.ts +++ b/test/commands/users/list.test.ts @@ -1,17 +1,29 @@ -import {expect, test} from '@oclif/test' +import { expect, test } from "@oclif/test"; +import { hasCosmosConnection } from "../../../src/utils/azure"; -describe('users:list', () => { - test - .stdout() - .command(['users:list']) - .it('runs hello', ctx => { - expect(ctx.stdout).to.contain('hello world') - }) - - test - .stdout() - .command(['users:list', '--name', 'jeff']) - .it('runs hello --name jeff', ctx => { - expect(ctx.stdout).to.contain('hello jeff') - }) -}) +describe("list all profiles", () => { + const isCosmosConnectionAvailable = hasCosmosConnection( + "agid-rg-test", + "agid-cosmosdb-test" + ); + /** + * if host has the az client installed and valid cosmos credentials we test command first output line matches + * the header row (fiscalCode column name). Otherwise the test expects for an error with 2 as exit status + */ + if (isCosmosConnectionAvailable) { + test + .stdout() + .command(["profiles:list"]) + .it("runs profiles command to list all users", ctx => { + expect(ctx.stdout).match(/^fiscalCode/); + }); + } else { + test + .stdout() + .command(["profiles:list"]) + .exit(2) + .it( + "check the command goes in error (no cosmos credential available or az client not installed)" + ); + } +}); diff --git a/test/mocha.opts b/test/mocha.opts index 73fb836..7686a30 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -2,4 +2,4 @@ --watch-extensions ts --recursive --reporter spec ---timeout 5000 +--timeout 15000 From 177126b11893bfc526c55cda40d4e6a293a4d2e7 Mon Sep 17 00:00:00 2001 From: Matteo Boschi Date: Mon, 17 Jun 2019 15:58:26 +0200 Subject: [PATCH 02/10] remove useless variable fix lint warnings --- src/commands/messages/attributes.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/commands/messages/attributes.ts b/src/commands/messages/attributes.ts index bcc98b2..0ec7be0 100644 --- a/src/commands/messages/attributes.ts +++ b/src/commands/messages/attributes.ts @@ -3,13 +3,11 @@ import { Command, flags } from "@oclif/command"; import cli from "cli-ux"; import * as parse from "csv-parse"; import * as fs from "fs"; -import * as t from "io-ts"; import * as transform from "stream-transform"; import { config, getCosmosEndpoint, - getCosmosReadonlyKey, getCosmosWriteKey } from "../../utils/azure"; import { parseMessagePath } from "../../utils/parser"; @@ -42,6 +40,7 @@ export default class MessagesAttributes extends Command { this.exit(); } + // tslint:disable-next-line: no-inferred-empty-object-type const messageDelta = [ { key: "isPending", @@ -95,7 +94,7 @@ export default class MessagesAttributes extends Command { cli.action.stop(); const client = new cosmos.CosmosClient({ endpoint, auth: { key } }); - const database = await client.database(config.cosmosDatabaseName); + const database = client.database(config.cosmosDatabaseName); const container = database.container(config.cosmosMessagesContainer); const updateMessage = async (fiscalCode: string, messageId: string) => { @@ -133,7 +132,7 @@ export default class MessagesAttributes extends Command { .pipe(transformer) .pipe(process.stdout); - await new Promise((res, rej) => parser.on("end", res)); + await new Promise((res, _) => parser.on("end", res)); } catch (e) { this.error(e); } From e33bc8e388a975f6a9af713a93c09b3118650532 Mon Sep 17 00:00:00 2001 From: Matteo Boschi Date: Mon, 17 Jun 2019 17:20:29 +0200 Subject: [PATCH 03/10] fix lint warnings --- src/commands/messages/attributes.ts | 2 +- src/commands/messages/check-content.ts | 24 ++++-------------------- src/commands/profiles/list.ts | 11 ++++++----- 3 files changed, 11 insertions(+), 26 deletions(-) diff --git a/src/commands/messages/attributes.ts b/src/commands/messages/attributes.ts index 0ec7be0..c9b5612 100644 --- a/src/commands/messages/attributes.ts +++ b/src/commands/messages/attributes.ts @@ -33,7 +33,7 @@ export default class MessagesAttributes extends Command { }; public run = async () => { - const { args, flags: parsedFlags } = this.parse(MessagesAttributes); + const { flags: parsedFlags } = this.parse(MessagesAttributes); if (parsedFlags.isPending === undefined) { this.error("At least one attribute must be changed"); diff --git a/src/commands/messages/check-content.ts b/src/commands/messages/check-content.ts index 22fa01d..c692700 100644 --- a/src/commands/messages/check-content.ts +++ b/src/commands/messages/check-content.ts @@ -1,19 +1,11 @@ -import * as cosmos from "@azure/cosmos"; import { Command, flags } from "@oclif/command"; import * as storage from "azure-storage"; import cli from "cli-ux"; import * as parse from "csv-parse"; import * as fs from "fs"; -import * as t from "io-ts"; -import { FiscalCode } from "italia-ts-commons/lib/strings"; import * as transform from "stream-transform"; -import { - config, - getCosmosEndpoint, - getCosmosReadonlyKey, - getStorageConnection -} from "../../utils/azure"; +import { config, getStorageConnection } from "../../utils/azure"; import { parseMessagePath } from "../../utils/parser"; export default class MessagesCheckContent extends Command { @@ -36,7 +28,7 @@ export default class MessagesCheckContent extends Command { }; public run = async () => { - const { args, flags: parsedFlags } = this.parse(MessagesCheckContent); + const { flags: parsedFlags } = this.parse(MessagesCheckContent); const inputStream = parsedFlags.input ? fs.createReadStream(parsedFlags.input) @@ -49,11 +41,7 @@ export default class MessagesCheckContent extends Command { try { cli.action.start("Retrieving credentials"); - const [endpoint, key, storageConnection] = await Promise.all([ - getCosmosEndpoint(config.resourceGroup, config.cosmosName), - getCosmosReadonlyKey(config.resourceGroup, config.cosmosName), - getStorageConnection(config.storageName) - ]); + const storageConnection = await getStorageConnection(config.storageName); cli.action.stop(); const blobService = storage.createBlobService(storageConnection); @@ -66,10 +54,6 @@ export default class MessagesCheckContent extends Command { (err, blobResult) => (err ? reject(err) : resolve(blobResult)) ) ); - // const client = new cosmos.CosmosClient({ endpoint, auth: { key } }); - // const database = await client.database(config.cosmosDatabaseName); - // const container = database.container(config.cosmosMessagesContainer); - const transformer = transform( { parallel: parsedFlags.parallel @@ -93,7 +77,7 @@ export default class MessagesCheckContent extends Command { .pipe(transformer) .pipe(process.stdout); - await new Promise((res, rej) => parser.on("end", res)); + await new Promise((res, _) => parser.on("end", res)); } catch (e) { this.error(e); } diff --git a/src/commands/profiles/list.ts b/src/commands/profiles/list.ts index 989f014..87ba809 100644 --- a/src/commands/profiles/list.ts +++ b/src/commands/profiles/list.ts @@ -1,18 +1,18 @@ import * as cosmos from "@azure/cosmos"; -import { Command, flags } from "@oclif/command"; +import { Command } from "@oclif/command"; import cli from "cli-ux"; import { getCosmosConnection } from "../../utils/azure"; export default class ProfilesList extends Command { - static description = "Lists all profiles"; + public static description = "Lists all profiles"; - static flags = { + public static flags = { ...cli.table.flags() }; - async run() { - const { args, flags } = this.parse(ProfilesList); + public async run(): Promise { + const { flags } = this.parse(ProfilesList); try { cli.action.start("Retrieving cosmosdb credentials"); @@ -48,6 +48,7 @@ export default class ProfilesList extends Command { }, createdAt: { header: "createdAt", + // tslint:disable-next-line: no-any get: (row: any) => row._ts && new Date(row._ts * 1000).toISOString(), extended: true From 7d5b419e05a6154fa8fafb92dff7a11e9114729d Mon Sep 17 00:00:00 2001 From: Matteo Boschi Date: Mon, 17 Jun 2019 17:29:04 +0200 Subject: [PATCH 04/10] fix lint warnings --- src/commands/hello.ts | 33 +++++++++++++------------- src/commands/messages/attributes.ts | 2 +- src/commands/messages/check-content.ts | 1 + src/commands/messages/list.ts | 7 +++--- src/index.ts | 2 +- src/utils/parser.ts | 1 - 6 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/commands/hello.ts b/src/commands/hello.ts index 8bb88d0..65a51af 100644 --- a/src/commands/hello.ts +++ b/src/commands/hello.ts @@ -1,31 +1,30 @@ -import {Command, flags} from '@oclif/command' +import { Command, flags } from "@oclif/command"; export default class Hello extends Command { - static description = 'describe the command here' + public static description = "describe the command here"; - static examples = [ + // tslint:disable-next-line: readonly-array + public static examples = [ `$ io-ops hello hello world from ./src/hello.ts! -`, - ] +` + ]; - static flags = { - help: flags.help({char: 'h'}), + public static flags = { + help: flags.help({ char: "h" }), // flag with a value (-n, --name=VALUE) - name: flags.string({char: 'n', description: 'name to print'}), + name: flags.string({ char: "n", description: "name to print" }), // flag with no value (-f, --force) - force: flags.boolean({char: 'f'}), - } - - static args = [{name: 'file'}] + force: flags.boolean({ char: "f" }) + }; - async run() { - const {args, flags} = this.parse(Hello) + public async run(): Promise { + const { args, flags: parsedFlags } = this.parse(Hello); - const name = flags.name || 'world' - this.log(`hello ${name} from ./src/commands/hello.ts`) + const name = parsedFlags.name || "world"; + this.log(`hello ${name} from ./src/commands/hello.ts`); if (args.file && flags.force) { - this.log(`you input --force and --file: ${args.file}`) + this.log(`you input --force and --file: ${args.file}`); } } } diff --git a/src/commands/messages/attributes.ts b/src/commands/messages/attributes.ts index c9b5612..1274cab 100644 --- a/src/commands/messages/attributes.ts +++ b/src/commands/messages/attributes.ts @@ -131,7 +131,7 @@ export default class MessagesAttributes extends Command { .pipe(parser) .pipe(transformer) .pipe(process.stdout); - + // tslint:disable-next-line: no-inferred-empty-object-type await new Promise((res, _) => parser.on("end", res)); } catch (e) { this.error(e); diff --git a/src/commands/messages/check-content.ts b/src/commands/messages/check-content.ts index c692700..13c1619 100644 --- a/src/commands/messages/check-content.ts +++ b/src/commands/messages/check-content.ts @@ -77,6 +77,7 @@ export default class MessagesCheckContent extends Command { .pipe(transformer) .pipe(process.stdout); + // tslint:disable-next-line: no-inferred-empty-object-type await new Promise((res, _) => parser.on("end", res)); } catch (e) { this.error(e); diff --git a/src/commands/messages/list.ts b/src/commands/messages/list.ts index 1e31010..68598e7 100644 --- a/src/commands/messages/list.ts +++ b/src/commands/messages/list.ts @@ -1,6 +1,5 @@ import * as cosmos from "@azure/cosmos"; -import { Command, flags } from "@oclif/command"; -import * as storage from "azure-storage"; +import { Command } from "@oclif/command"; import cli from "cli-ux"; import { FiscalCode } from "italia-ts-commons/lib/strings"; @@ -46,11 +45,11 @@ export default class MessagesList extends Command { cli.action.stop(); const client = new cosmos.CosmosClient({ endpoint, auth: { key } }); - const database = await client.database(config.cosmosDatabaseName); + const database = client.database(config.cosmosDatabaseName); const container = database.container(config.cosmosMessagesContainer); cli.action.start("Querying messages..."); - const response = await container.items.query({ + const response = container.items.query({ parameters: [{ name: "@fiscalCode", value: fiscalCode }], query: "SELECT c.id, c.createdAt, c.isPending FROM c WHERE c.fiscalCode = @fiscalCode" diff --git a/src/index.ts b/src/index.ts index 4caa481..8bdb76f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1 @@ -export {run} from '@oclif/command' +export { run } from "@oclif/command"; diff --git a/src/utils/parser.ts b/src/utils/parser.ts index 4b7d9e5..43d5fbd 100644 --- a/src/utils/parser.ts +++ b/src/utils/parser.ts @@ -1,5 +1,4 @@ import * as t from "io-ts"; -import { FiscalCode } from "italia-ts-commons/lib/strings"; export const parseMessagePath = ( value: unknown From 4dd57d3039e7500db348e209c83c3d19c1bd69c4 Mon Sep 17 00:00:00 2001 From: Matteo Boschi Date: Mon, 17 Jun 2019 17:33:31 +0200 Subject: [PATCH 05/10] fix lint warnings --- src/commands/hello.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/hello.ts b/src/commands/hello.ts index 65a51af..fc08837 100644 --- a/src/commands/hello.ts +++ b/src/commands/hello.ts @@ -23,7 +23,7 @@ hello world from ./src/hello.ts! const name = parsedFlags.name || "world"; this.log(`hello ${name} from ./src/commands/hello.ts`); - if (args.file && flags.force) { + if (args.file && parsedFlags.force) { this.log(`you input --force and --file: ${args.file}`); } } From a1cb99bb030b1ce63cc364d23780c4298cfd2be8 Mon Sep 17 00:00:00 2001 From: Matteo Boschi Date: Mon, 17 Jun 2019 17:58:12 +0200 Subject: [PATCH 06/10] fix tests --- src/utils/azure.ts | 16 +++++++++---- test/commands/users/list.test.ts | 39 +++++++++++++++----------------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/utils/azure.ts b/src/utils/azure.ts index 543efcf..f28929f 100644 --- a/src/utils/azure.ts +++ b/src/utils/azure.ts @@ -47,15 +47,23 @@ export const getStorageConnection = async (name: string) => * hasCosmosConnection checks if the host has az cli installed and resouceGroup and name * are valid inputs for cosmosdb */ -export const hasCosmosConnection = (resourceGroup: string, name: string) => { +export const hasCosmosConnection = async ( + resourceGroup: string, + name: string +) => { try { - execa.sync( + const successExitCode = "SUCCESS"; + const documentEndpoint = await execa( `az cosmosdb show -g ${resourceGroup} -n ${name} --query documentEndpoint -o tsv` ); - execa.sync( + const primaryReadonlyMasterKey = execa.sync( `az cosmosdb list-keys -g ${resourceGroup} -n ${name} --query primaryReadonlyMasterKey -o tsv` ); - return true; + + return ( + documentEndpoint.exitCodeName === successExitCode && + primaryReadonlyMasterKey.exitCodeName === successExitCode + ); } catch (e) { return false; } diff --git a/test/commands/users/list.test.ts b/test/commands/users/list.test.ts index be4fd6e..ee60444 100644 --- a/test/commands/users/list.test.ts +++ b/test/commands/users/list.test.ts @@ -2,28 +2,25 @@ import { expect, test } from "@oclif/test"; import { hasCosmosConnection } from "../../../src/utils/azure"; describe("list all profiles", () => { - const isCosmosConnectionAvailable = hasCosmosConnection( - "agid-rg-test", - "agid-cosmosdb-test" - ); /** * if host has the az client installed and valid cosmos credentials we test command first output line matches - * the header row (fiscalCode column name). Otherwise the test expects for an error with 2 as exit status + * the header row (fiscalCode column name). Otherwise the test will be skipped */ - if (isCosmosConnectionAvailable) { - test - .stdout() - .command(["profiles:list"]) - .it("runs profiles command to list all users", ctx => { - expect(ctx.stdout).match(/^fiscalCode/); - }); - } else { - test - .stdout() - .command(["profiles:list"]) - .exit(2) - .it( - "check the command goes in error (no cosmos credential available or az client not installed)" - ); - } + + before(async function(): Promise { + const isCosmosConnectionAvailable = await hasCosmosConnection( + "agid-rg-test", + "agid-cosmosdb-test" + ); + if (!isCosmosConnectionAvailable) { + // tslint:disable-next-line: no-invalid-this + this.skip(); + } + }); + test + .stdout() + .command(["profiles:list"]) + .it("runs profiles command to list all users", ctx => { + expect(ctx.stdout).match(/^fiscalCode/); + }); }); From 2f168e851449e527abe49960e2ef648821bf47d9 Mon Sep 17 00:00:00 2001 From: Matteo Boschi Date: Tue, 18 Jun 2019 12:05:21 +0200 Subject: [PATCH 07/10] fix hasCosmosConnection function --- src/utils/azure.ts | 2 +- test/commands/users/list.test.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/azure.ts b/src/utils/azure.ts index f28929f..d8b4413 100644 --- a/src/utils/azure.ts +++ b/src/utils/azure.ts @@ -56,7 +56,7 @@ export const hasCosmosConnection = async ( const documentEndpoint = await execa( `az cosmosdb show -g ${resourceGroup} -n ${name} --query documentEndpoint -o tsv` ); - const primaryReadonlyMasterKey = execa.sync( + const primaryReadonlyMasterKey = await execa( `az cosmosdb list-keys -g ${resourceGroup} -n ${name} --query primaryReadonlyMasterKey -o tsv` ); diff --git a/test/commands/users/list.test.ts b/test/commands/users/list.test.ts index ee60444..0ed8679 100644 --- a/test/commands/users/list.test.ts +++ b/test/commands/users/list.test.ts @@ -12,6 +12,7 @@ describe("list all profiles", () => { "agid-rg-test", "agid-cosmosdb-test" ); + // az cli not installed or bad cosmos credential, test no needed if (!isCosmosConnectionAvailable) { // tslint:disable-next-line: no-invalid-this this.skip(); From 2351d400917dbb2f408aeea4451feb74304dc860 Mon Sep 17 00:00:00 2001 From: Matteo Boschi Date: Thu, 20 Jun 2019 17:21:33 +0200 Subject: [PATCH 08/10] add error logging in catch block --- src/utils/azure.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/azure.ts b/src/utils/azure.ts index d8b4413..403edd7 100644 --- a/src/utils/azure.ts +++ b/src/utils/azure.ts @@ -1,4 +1,5 @@ import * as execa from "execa"; +import cli from "cli-ux"; export const config = { cosmosDatabaseName: "agid-documentdb-test", @@ -65,6 +66,7 @@ export const hasCosmosConnection = async ( primaryReadonlyMasterKey.exitCodeName === successExitCode ); } catch (e) { + cli.error(e); return false; } }; From b220368cda6bd59af364ded163392d296c2757ce Mon Sep 17 00:00:00 2001 From: Matteo Boschi Date: Thu, 20 Jun 2019 17:23:05 +0200 Subject: [PATCH 09/10] add error logging (log) in catch block --- src/utils/azure.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/azure.ts b/src/utils/azure.ts index 403edd7..0e9e7ac 100644 --- a/src/utils/azure.ts +++ b/src/utils/azure.ts @@ -66,7 +66,7 @@ export const hasCosmosConnection = async ( primaryReadonlyMasterKey.exitCodeName === successExitCode ); } catch (e) { - cli.error(e); + cli.log(e); return false; } }; From 5ce36b566b9b049ee6d7618c00f7981da6ee4370 Mon Sep 17 00:00:00 2001 From: Matteo Boschi Date: Thu, 20 Jun 2019 17:24:13 +0200 Subject: [PATCH 10/10] fix import --- src/utils/azure.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/azure.ts b/src/utils/azure.ts index 0e9e7ac..a1782b9 100644 --- a/src/utils/azure.ts +++ b/src/utils/azure.ts @@ -1,5 +1,5 @@ -import * as execa from "execa"; import cli from "cli-ux"; +import * as execa from "execa"; export const config = { cosmosDatabaseName: "agid-documentdb-test",