Skip to content

Commit

Permalink
Add a test number which when called says hello, world!
Browse files Browse the repository at this point in the history
  • Loading branch information
Zemnmez committed Feb 16, 2024
1 parent cd48150 commit f13f990
Show file tree
Hide file tree
Showing 6 changed files with 255 additions and 1 deletion.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@
"@commander-js/extra-typings": "12.0.0",
"@next/eslint-plugin-next": "14.1.0",
"@pulumi/command": "4.5.0",
"@pulumi/random": "^4.15.1",
"@react-spring/rafz": "9.7.3",
"@tanstack/react-query": "4.36.1",
"@types/bcryptjs": "2.4.6",
"@types/memoizee": "0.4.11",
"@types/pako": "2.0.3",
Expand All @@ -127,7 +129,6 @@
"npm": "10.4.0",
"pako": "2.1.0",
"pnpm": "^8.0.0",
"@tanstack/react-query": "4.36.1",
"seedrandom": "3.0.5",
"selenium-webdriver": "4.17.0",
"serve-handler": "6.1.5"
Expand Down
11 changes: 11 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ts/pulumi/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ ts_project(
deps = [
"//:node_modules/@pulumi/aws",
"//:node_modules/@pulumi/pulumi",
"//:node_modules/@pulumi/random",
"//:node_modules/@types/cross-spawn",
"//:node_modules/@types/jest",
"//:node_modules/cross-spawn",
Expand Down
120 changes: 120 additions & 0 deletions ts/pulumi/lib/contact_flow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import {
ContactFlow as BaseContactFlow,
ContactFlowArgs,
} from '@pulumi/aws/connect/index.js';
import {
all,
ComponentResource,
ComponentResourceOptions,
Input,
} from '@pulumi/pulumi';

import { Err, Ok, Result } from '#root/ts/result.js';

interface ActionBase {
Identifier: string;
Type: string;
Parameters: unknown;
Transitions?: {
NextAction?: string;
Errors?: string[];
Conditions?: string[];
};
}

export interface EndFlowExecutionAction extends ActionBase {
Type: 'DisconnectParticipant';
Parameters: Record<string, never>;
Transitions?: Record<string, never>;
}

export interface MessageParticipantAction extends ActionBase {
Type: 'MessageParticipant';
Parameters: {
/**
* A prompt ID or prompt ARN to play to the participant along with gathering input. May not be specified if Text or SSML is also specified.
* Must be specified either statically or as a single valid JSONPath identifier
*/
PromptId?: string;
/**
* An optional string that defines text to send to the participant along with gathering input.
* May not be specified if PromptId or SSML is also specified. May be specified statically or dynamically.
*/
Text?: string;
/**
* An optional string that defines SSML to send to the participant along with gathering input. May not be specified if Text or
* PromptId is also specified May be specified statically or dynamically.
*/
SSML?: string;
media?: {
uri: string;
SourceType: 'S3';
MediaType: 'Audio';
};
};
Transitions: {
NextAction: string;
};
}

export type ContactFlowAction =
| MessageParticipantAction
| EndFlowExecutionAction;

export interface ContactFlowLanguage {
Version: '2019-10-30';
StartAction: string;
Actions: ContactFlowAction[];
}

export interface Args extends Omit<ContactFlowArgs, 'content'> {
content: Input<ContactFlowLanguage>;
}

/**
* Creates a ContactFlowModule, but it's typechecked.
*/
export class ContactFlow extends ComponentResource {
readonly value: BaseContactFlow;
constructor(
name: string,
{ content, ...args }: Args,
opts?: ComponentResourceOptions
) {
super('ts:pulumi:lib:ContactFlowModule', name, args, opts);

void ContactFlow.validate(content).then(v => {
if (v instanceof Error) throw v;
});

this.value = new BaseContactFlow(
`${name}_contact_flow_module`,
{
...args,
content: all([content]).apply(([v]) => JSON.stringify(v)),
},
{ parent: this }
);
}

private static async validateEntryPointSet(
flow: ContactFlowLanguage
): Promise<Result<void, Error>> {
if (!flow.Actions.some(v => v.Identifier == flow.StartAction))
return {
[Err]: new Error(`Missing entry point ${flow.StartAction}`),
};

return { [Ok]: undefined };
}

private static async validate(
v: Input<ContactFlowLanguage>
): Promise<Result<void, Error>> {
const flow = await new Promise<ContactFlowLanguage>(ok =>
all([v]).apply(([flow]) => ok(flow!))
);

return ContactFlow.validateEntryPointSet(flow);
}
}
3 changes: 3 additions & 0 deletions ts/pulumi/zemn.me/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as Pulumi from '@pulumi/pulumi';

import { mergeTags, tagTrue } from '#root/ts/pulumi/lib/tags.js';
import Website from '#root/ts/pulumi/lib/website.js';
import { Voice } from '#root/ts/pulumi/zemn.me/voice/voice.js';

//

Expand Down Expand Up @@ -33,6 +34,8 @@ export class Component extends Pulumi.ComponentResource {
{ parent: this }
);

new Voice(`${name}_voice`, { tags }, { parent: this });

this.site = new Website(
`${name}_zemn_me`,
{
Expand Down
118 changes: 118 additions & 0 deletions ts/pulumi/zemn.me/voice/voice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import * as aws from '@pulumi/aws';
import * as Pulumi from '@pulumi/pulumi';
import { RandomPet } from '@pulumi/random';

import {
ContactFlow,
ContactFlowAction,
ContactFlowLanguage,
} from '#root/ts/pulumi/lib/contact_flow.js';
import { mergeTags, tagTrue } from '#root/ts/pulumi/lib/tags.js';

export interface Args {
tags?: Pulumi.Input<Record<string, Pulumi.Input<string>>>;
}

export class Voice extends Pulumi.ComponentResource {
phoneNumber: Pulumi.Output<string>;
constructor(
name: string,
args: Args,
opts?: Pulumi.ComponentResourceOptions
) {
super('ts:pulumi:voice', name, args, opts);
const tag = name;
const tags = mergeTags(args.tags, tagTrue(tag));

/*
new CostAllocationTag(
`${name}_cost_tag`,
{
status: 'Active',
tagKey: tag,
},
{ parent: this }
);
*/

const connectInstance = new aws.connect.Instance(
`${name}_connect_instance`,
{
inboundCallsEnabled: true,
outboundCallsEnabled: true,
identityManagementType: 'CONNECT_MANAGED',
instanceAlias: `${name}_connect_instance`.replaceAll(
/[^a-z]/g,
''
),
},
{ parent: this }
);

const disconnectAction = new RandomPet(
`${name}_disconnect_flow_id`,
{},
{ parent: this }
).id.apply(
id =>
({
Identifier: id,
Type: 'DisconnectParticipant',
Parameters: {},
}) satisfies ContactFlowAction
);

const action = Pulumi.all([
new RandomPet(`${name}_flow_id`, {}, { parent: this }).id,
disconnectAction,
]).apply(
([Identifier, disconnectAction]) =>
({
Identifier,
Type: 'MessageParticipant',
Parameters: {
Text: 'Hello, world!',
},
Transitions: {
NextAction: disconnectAction.Identifier,
},
}) satisfies ContactFlowAction
);

const flow: Pulumi.Input<ContactFlowLanguage> = Pulumi.all([
action,
disconnectAction,
]).apply(
([action, disconnectAction]) =>
({
Version: '2019-10-30',
StartAction: action!.Identifier,
Actions: [action!, disconnectAction],
}) satisfies ContactFlowLanguage
);

new ContactFlow(
`${name}_contact_flow`,
{
instanceId: connectInstance.id,
name: 'Hello world flow',
type: 'CONTACT_FLOW',
content: flow,
},
{ parent: this }
);

const phone = new aws.connect.PhoneNumber(
`${name}_phone_number`,
{
countryCode: 'US',
type: 'DID',
targetArn: connectInstance.arn,
tags,
},
{ parent: this }
);

this.phoneNumber = phone.phoneNumber;
}
}

0 comments on commit f13f990

Please sign in to comment.