-
Notifications
You must be signed in to change notification settings - Fork 1
feat(audience): add full audience resource support #730
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
a9f32cc
feat(audience): add full audience resource support
meryldakin fb5233d
test: add audience stubs to pull/push tests
meryldakin f66cd4a
fix: remove debug console.log from audience writer
meryldakin e81d80f
fix: address code review feedback for audience implementation
meryldakin 63c4850
fix: use else-if chain in resource dir detection to prevent fallthrough
meryldakin 20ee892
fix: forward branch and environment flags when pushing from audience new
meryldakin 97088bd
fix: allow audience push and validate in any environment
meryldakin 34b38bf
chore: remove orphaned TODO comment from audience validate
meryldakin c37b8c2
fix: remove default segments from dynamic audience scaffolding
meryldakin 0510cbc
fix: use ux.error instead of throw for consistent error handling
meryldakin 47654e1
fix: add missing error check in pullOneAudience
meryldakin a169fe0
update
meryldakin 54ebab0
update
meryldakin da91820
update
meryldakin 7f8a623
update
meryldakin df29774
update
meryldakin ebafa61
update
meryldakin 3b645d4
update
meryldakin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import { Args, Flags, ux } from "@oclif/core"; | ||
|
|
||
| import BaseCommand from "@/lib/base-command"; | ||
| import { ApiError } from "@/lib/helpers/error"; | ||
| import * as CustomFlags from "@/lib/helpers/flag"; | ||
| import { promptToConfirm, spinner } from "@/lib/helpers/ux"; | ||
|
|
||
| export default class AudienceArchive extends BaseCommand< | ||
| typeof AudienceArchive | ||
| > { | ||
| static summary = "Archive an audience (affects ALL environments)."; | ||
|
|
||
| static description = ` | ||
| WARNING: Archiving an audience affects ALL environments and cannot be undone. | ||
| Use this command with caution. | ||
| `; | ||
|
|
||
| static flags = { | ||
| environment: Flags.string({ | ||
| required: true, | ||
| summary: "The environment to use.", | ||
| }), | ||
| branch: CustomFlags.branch, | ||
| force: Flags.boolean({ | ||
| summary: "Skip confirmation prompt.", | ||
| }), | ||
| }; | ||
|
|
||
| static args = { | ||
| audienceKey: Args.string({ | ||
| required: true, | ||
| description: "The key of the audience to archive.", | ||
| }), | ||
| }; | ||
|
|
||
| async run(): Promise<void> { | ||
| const { audienceKey } = this.props.args; | ||
| const { force, environment } = this.props.flags; | ||
|
|
||
| // Confirm before archiving since this affects all environments | ||
| if (!force) { | ||
| const confirmed = await promptToConfirm( | ||
| `WARNING: Archiving audience \`${audienceKey}\` will affect ALL environments.\n` + | ||
| `This action cannot be undone. Continue?`, | ||
| ); | ||
| if (!confirmed) { | ||
| this.log("Archive cancelled."); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| spinner.start(`‣ Archiving audience \`${audienceKey}\``); | ||
|
|
||
| try { | ||
| await this.apiV1.mgmtClient.audiences.archive(audienceKey, { | ||
| environment, | ||
| }); | ||
|
|
||
| spinner.stop(); | ||
|
|
||
| this.log( | ||
| `‣ Successfully archived audience \`${audienceKey}\` across all environments.`, | ||
| ); | ||
| } catch (error) { | ||
| spinner.stop(); | ||
| ux.error(new ApiError((error as Error).message)); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| import { Audience } from "@knocklabs/mgmt/resources/audiences"; | ||
| import { Args, Flags, ux } from "@oclif/core"; | ||
|
|
||
| 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 { 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<Audience | 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: Audience; | ||
| }> { | ||
| const { audienceKey } = this.props.args; | ||
| const { flags } = this.props; | ||
|
|
||
| try { | ||
| const audience = await this.apiV1.mgmtClient.audiences.retrieve( | ||
| audienceKey, | ||
| { | ||
| environment: flags.environment, | ||
| branch: flags.branch, | ||
| hide_uncommitted_changes: flags["hide-uncommitted-changes"], | ||
| }, | ||
| ); | ||
|
|
||
| return { audience }; | ||
| } catch (error) { | ||
| ux.error(new ApiError((error as Error).message)); | ||
| } | ||
| } | ||
|
|
||
| render(audience: Audience): 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)); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| import { Audience } from "@knocklabs/mgmt/resources/audiences"; | ||
| import { Flags, ux } from "@oclif/core"; | ||
|
|
||
| import BaseCommand from "@/lib/base-command"; | ||
| import { formatCommandScope } from "@/lib/helpers/command"; | ||
| import { formatDate } from "@/lib/helpers/date"; | ||
| import { ApiError } from "@/lib/helpers/error"; | ||
| import * as CustomFlags from "@/lib/helpers/flag"; | ||
| import { | ||
| maybePromptPageAction, | ||
| pageFlags, | ||
| PageInfo, | ||
| paramsForPageAction, | ||
| } from "@/lib/helpers/page"; | ||
| import { spinner } from "@/lib/helpers/ux"; | ||
|
|
||
| type ListAudienceData = { | ||
| entries: Audience[]; | ||
| page_info: PageInfo; | ||
| }; | ||
|
|
||
| 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<ListAudienceData | void> { | ||
| const data = await this.request(); | ||
|
|
||
| const { flags } = this.props; | ||
| if (flags.json) return data; | ||
|
|
||
| return this.render(data); | ||
| } | ||
|
|
||
| async request( | ||
| pageParams: { after?: string; before?: string } = {}, | ||
| ): Promise<ListAudienceData> { | ||
| const { flags } = this.props; | ||
|
|
||
| spinner.start("‣ Loading"); | ||
|
|
||
| try { | ||
| const page = await this.apiV1.mgmtClient.audiences.list({ | ||
| environment: flags.environment, | ||
| branch: flags.branch, | ||
| hide_uncommitted_changes: flags["hide-uncommitted-changes"], | ||
| limit: flags.limit, | ||
| after: pageParams.after ?? flags.after, | ||
| before: pageParams.before ?? flags.before, | ||
| }); | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| spinner.stop(); | ||
|
|
||
| return { | ||
| entries: page.entries, | ||
| page_info: page.page_info, | ||
| }; | ||
| } catch (error) { | ||
| spinner.stop(); | ||
| throw new ApiError((error as Error).message); | ||
| } | ||
| } | ||
|
|
||
| async render(data: ListAudienceData): 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 as unknown as Record<string, unknown>[], { | ||
| key: { | ||
| header: "Key", | ||
| }, | ||
| name: { | ||
| header: "Name", | ||
| }, | ||
| description: { | ||
| header: "Description", | ||
| }, | ||
| type: { | ||
| header: "Type", | ||
| }, | ||
| updated_at: { | ||
| header: "Updated at", | ||
| get: (entry) => formatDate(entry.updated_at as string), | ||
| }, | ||
| }); | ||
|
|
||
| return this.prompt(data); | ||
| } | ||
|
|
||
| async prompt(data: ListAudienceData): 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 nextData = await this.request(pageParams); | ||
| return this.render(nextData); | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.