Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
123 changes: 123 additions & 0 deletions src/commands/audience/get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { Args, Flags, ux } from "@oclif/core";

import * as ApiV1 from "@/lib/api-v1";
import BaseCommand from "@/lib/base-command";
import { formatCommandScope } from "@/lib/helpers/command";
import { formatDateTime } from "@/lib/helpers/date";
import { ApiError } from "@/lib/helpers/error";
import * as CustomFlags from "@/lib/helpers/flag";
import { formatErrorRespMessage, isSuccessResp } from "@/lib/helpers/request";
import { spinner } from "@/lib/helpers/ux";

export default class AudienceGet extends BaseCommand<typeof AudienceGet> {
static summary = "Display a single audience from an environment.";

static flags = {
environment: Flags.string({
default: "development",
summary: "The environment to use.",
}),
branch: CustomFlags.branch,
"hide-uncommitted-changes": Flags.boolean({
summary: "Hide any uncommitted changes.",
}),
};

static args = {
audienceKey: Args.string({
required: true,
}),
};

static enableJsonFlag = true;

async run(): Promise<ApiV1.GetAudienceResp | void> {
spinner.start("‣ Loading");

const { audience } = await this.loadAudience();

spinner.stop();

const { flags } = this.props;
if (flags.json) return audience;

this.render(audience);
}

private async loadAudience(): Promise<{
audience: ApiV1.GetAudienceResp;
}> {
const audienceResp = await this.apiV1.getAudience(this.props);

if (!isSuccessResp(audienceResp)) {
const message = formatErrorRespMessage(audienceResp);
ux.error(new ApiError(message));
}

return {
audience: audienceResp.data,
};
}

render(audience: ApiV1.GetAudienceResp): void {
const { audienceKey } = this.props.args;
const { environment: env, "hide-uncommitted-changes": committedOnly } =
this.props.flags;

const qualifier =
env === "development" && !committedOnly ? "(including uncommitted)" : "";

const scope = formatCommandScope(this.props.flags);
this.log(
`‣ Showing audience \`${audienceKey}\` in ${scope} ${qualifier}\n`,
);

/*
* Audience table
*/

const rows = [
{
key: "Name",
value: audience.name,
},
{
key: "Key",
value: audience.key,
},
{
key: "Type",
value: audience.type,
},
{
key: "Description",
value: audience.description || "-",
},
{
key: "Created at",
value: formatDateTime(audience.created_at),
},
{
key: "Updated at",
value: formatDateTime(audience.updated_at),
},
];

ux.table(rows, {
key: {
header: "Audience",
minWidth: 24,
},
value: {
header: "",
minWidth: 24,
},
});

// Show segments for dynamic audiences
if (audience.type === "dynamic" && audience.segments) {
this.log("\nSegments:");
this.log(JSON.stringify(audience.segments, null, 2));
}
}
}
105 changes: 105 additions & 0 deletions src/commands/audience/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { Flags, ux } from "@oclif/core";
import { AxiosResponse } from "axios";

import * as ApiV1 from "@/lib/api-v1";
import BaseCommand from "@/lib/base-command";
import { formatCommandScope } from "@/lib/helpers/command";
import { formatDate } from "@/lib/helpers/date";
import * as CustomFlags from "@/lib/helpers/flag";
import { merge } from "@/lib/helpers/object.isomorphic";
import {
maybePromptPageAction,
pageFlags,
paramsForPageAction,
} from "@/lib/helpers/page";
import { withSpinner } from "@/lib/helpers/request";

export default class AudienceList extends BaseCommand<typeof AudienceList> {
static summary = "Display all audiences for an environment.";

static flags = {
environment: Flags.string({
default: "development",
summary: "The environment to use.",
}),
branch: CustomFlags.branch,
"hide-uncommitted-changes": Flags.boolean({
summary: "Hide any uncommitted changes.",
}),
...pageFlags,
};

static enableJsonFlag = true;

async run(): Promise<ApiV1.ListAudienceResp | void> {
const resp = await this.request();

const { flags } = this.props;
if (flags.json) return resp.data;

this.render(resp.data);
}

async request(
pageParams = {},
): Promise<AxiosResponse<ApiV1.ListAudienceResp>> {
const props = merge(this.props, { flags: { ...pageParams } });

return withSpinner<ApiV1.ListAudienceResp>(() =>
this.apiV1.listAudiences(props),
);
}

async render(data: ApiV1.ListAudienceResp): Promise<void> {
const { entries } = data;
const { environment: env, "hide-uncommitted-changes": committedOnly } =
this.props.flags;

const qualifier =
env === "development" && !committedOnly ? "(including uncommitted)" : "";

const scope = formatCommandScope(this.props.flags);
this.log(
`‣ Showing ${entries.length} audiences in ${scope} ${qualifier}\n`,
);

/*
* Audiences list table
*/

ux.table(entries, {
key: {
header: "Key",
},
name: {
header: "Name",
},
description: {
header: "Description",
},
type: {
header: "Type",
},
updated_at: {
header: "Updated at",
get: (entry) => formatDate(entry.updated_at),
},
});

return this.prompt(data);
}

async prompt(data: ApiV1.ListAudienceResp): Promise<void> {
const { page_info } = data;

const pageAction = await maybePromptPageAction(page_info);
const pageParams = pageAction && paramsForPageAction(pageAction, page_info);

if (pageParams) {
this.log("\n");

const resp = await this.request(pageParams);
return this.render(resp.data);
}
}
}
53 changes: 53 additions & 0 deletions src/commands/audience/open.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Args, Flags, ux } from "@oclif/core";

import BaseCommand from "@/lib/base-command";
import { browser } from "@/lib/helpers/browser";
import { ApiError } from "@/lib/helpers/error";
import * as CustomFlags from "@/lib/helpers/flag";
import { formatErrorRespMessage, isSuccessResp } from "@/lib/helpers/request";
import { viewAudienceUrl } from "@/lib/urls";

export default class AudienceOpen extends BaseCommand<typeof AudienceOpen> {
static summary = "Open an audience in the Knock dashboard.";

static flags = {
environment: Flags.string({
default: "development",
summary: "The environment to use.",
}),
branch: CustomFlags.branch,
};

static args = {
audienceKey: Args.string({
required: true,
}),
};

async run(): Promise<void> {
const whoamiResp = await this.apiV1.whoami();

if (!isSuccessResp(whoamiResp)) {
const message = formatErrorRespMessage(whoamiResp);
ux.error(new ApiError(message));
}

const { account_slug } = whoamiResp.data;
const { audienceKey } = this.props.args;
const { environment, branch } = this.props.flags;

const envOrBranch = branch ?? environment;

const url = viewAudienceUrl(
this.sessionContext.dashboardOrigin,
account_slug,
envOrBranch,
audienceKey,
);

this.log(`‣ Opening audience \`${audienceKey}\` in the Knock dashboard...`);
this.log(` ${url}`);

await browser.openUrl(url);
}
}
Loading
Loading