Repository navigation
Preserve authored confirmed campaign test-send command #96
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import {Args, Flags} from '@oclif/core' | ||
|
|
||
| import {readJsonInput} from '../../api/request.js' | ||
| import {BaseCommand} from '../../base-command.js' | ||
| import { | ||
| renderCampaignTestSend, | ||
| testSendCampaign, | ||
| } from '../../services/campaigns.js' | ||
|
|
||
| export default class CampaignTestSend extends BaseCommand { | ||
| static override args = { | ||
| campaign: Args.string({description: 'Draft campaign ID.', required: true}), | ||
| } | ||
|
|
||
| static override description = | ||
| 'Send one draft campaign test to a team-connected seed number.' | ||
|
|
||
| static override flags = { | ||
| 'idempotency-key': Flags.string({ | ||
| description: | ||
| 'Required deduplication key. Reuse this key after an uncertain result; never retry with a new key before checking the seed phone.', | ||
| required: true, | ||
| }), | ||
| input: Flags.string({ | ||
| description: | ||
| 'Read one JSON object with sessionId, to, and optional sampleRecipientId or variables from a file or stdin (-).', | ||
| required: true, | ||
| }), | ||
| 'project-slug': Flags.string({ | ||
| description: 'Project slug in the Messaging API path.', | ||
| required: true, | ||
| }), | ||
| yes: Flags.boolean({ | ||
| description: | ||
| 'Confirm sending one real WhatsApp message to the seed number.', | ||
| required: true, | ||
| }), | ||
| } | ||
|
|
||
| async run(): Promise<void> { | ||
| const {args, flags} = await this.parse(CampaignTestSend) | ||
| const result = await testSendCampaign( | ||
| await this.messagingClient(flags, {serverApiKeyOnly: 'Campaign'}), | ||
| flags['project-slug'], | ||
| args.campaign, | ||
| await readJsonInput(flags.input), | ||
| {idempotencyKey: flags['idempotency-key']}, | ||
| ) | ||
| this.writeValue(result, renderCampaignTestSend(result.data), flags) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,8 @@ | |
| type CampaignRecipientStatus, | ||
| type CampaignRequeueResult, | ||
| type CampaignStopOperation, | ||
| type CampaignTestSend, | ||
| type CampaignTestSendRequest, | ||
| type CreateCampaignRequest, | ||
| type InvalidRecipientRow, | ||
| type ListCampaignRecipientsParams, | ||
|
|
@@ -20,8 +22,8 @@ | |
|
|
||
| export interface CampaignResult<T> { | ||
| readonly data: T | ||
| readonly metadata: { | ||
|
Check failure on line 25 in src/services/campaigns.ts
|
||
| readonly apiVersion?: string | ||
|
Check failure on line 26 in src/services/campaigns.ts
|
||
| readonly attempts: number | ||
| readonly requestId?: string | ||
| readonly status: number | ||
|
|
@@ -84,11 +86,27 @@ | |
| return result(response.data.data, response.metadata) | ||
| } | ||
|
|
||
| export async function testSendCampaign( | ||
| client: MessagingClient, | ||
| projectSlug: string, | ||
| campaignId: string, | ||
| input: unknown, | ||
| options: RequestOptions, | ||
| ): Promise<CampaignResult<CampaignTestSend>> { | ||
| const response = await client.campaigns.testSend( | ||
| projectSlug, | ||
| campaignId, | ||
| campaignTestSendInput(input), | ||
| options, | ||
| ) | ||
| return result(response.data.data, response.metadata) | ||
| } | ||
|
|
||
| export async function pauseCampaign( | ||
| client: MessagingClient, | ||
| projectSlug: string, | ||
| campaignId: string, | ||
| options: RequestOptions = {}, | ||
|
Check failure on line 109 in src/services/campaigns.ts
|
||
| ): Promise<CampaignResult<CampaignOperation>> { | ||
| const response = await client.campaigns.pause( | ||
| projectSlug, | ||
|
|
@@ -258,6 +276,10 @@ | |
| return `Requeued ${result.requeued} ${result.requeued === 1 ? 'recipient' : 'recipients'}.` | ||
| } | ||
|
|
||
| export function renderCampaignTestSend(testSend: CampaignTestSend): string { | ||
| return `Sent one campaign test to ${testSend.to} from ${testSend.from.phone}. Test ID: ${testSend.id}.` | ||
| } | ||
|
|
||
| /** | ||
| * Stop answers with a null operation ID when the campaign had no delivery run | ||
| * to stop. Saying "accepted as operation null" would be untrue, so the two | ||
|
|
@@ -351,6 +373,22 @@ | |
| return input as unknown as CreateCampaignRequest | ||
| } | ||
|
|
||
| function campaignTestSendInput(input: unknown): CampaignTestSendRequest { | ||
| if ( | ||
| !isRecord(input) || | ||
| typeof input.sessionId !== 'string' || | ||
| input.sessionId.trim().length === 0 || | ||
| typeof input.to !== 'string' || | ||
| input.to.trim().length === 0 | ||
| ) { | ||
| throw new PolymorfaValidationError( | ||
| 'Campaign test input must contain one JSON object with sessionId and to.', | ||
| {code: 'invalid_campaign_test_input'}, | ||
| ) | ||
| } | ||
| return input as unknown as CampaignTestSendRequest | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate the request before casting it. The JSON input can contain Based on learnings, external input needs runtime validation; a TypeScript assertion does not provide it.
|
||
| } | ||
|
|
||
| function result<T>(data: T, metadata: ResponseMetadata): CampaignResult<T> { | ||
| return { | ||
| data, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use an SDK that exposes the test-send contract.
The declared
@polymorfa/sdkversion lacks both imported types andMessagingCampaignsResource.testSend. CI reports TS2305 and TS2339 duringnpm ci’spreparebuild. This blocks installation and building of the CLI. Align the SDK dependency and lockfile with an available implementation before publishing this command. (github.com)Source: Pipeline failures