Skip to content
Merged
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
2 changes: 1 addition & 1 deletion integrations/whatsapp/integration.definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ const defaultBotPhoneNumberId = {

export default new IntegrationDefinition({
name: INTEGRATION_NAME,
version: '4.5.0',
version: '4.5.1',
title: 'WhatsApp',
description: 'Send and receive messages through WhatsApp.',
icon: 'icon.svg',
Expand Down
29 changes: 7 additions & 22 deletions integrations/whatsapp/src/webhook/handlers/sandbox.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,20 @@
import { z } from '@botpress/sdk'
import {
CONVERSATION_CONNECTED_MESSAGE,
CONVERSATION_DISCONNECTED_MESSAGE,
extractSandboxCommand,
} from '@botpress/common'
import { getAuthenticatedWhatsappClient } from 'src/auth'
import { WhatsAppPayloadSchema, WhatsAppMessageValue } from 'src/misc/types'
import { Text } from 'whatsapp-api-js/messages'
import * as bp from '.botpress'

const supportedDynamicLinkingCommandsSchema = z.enum(['join', 'leave'])
type SupportedDynamicLinkingCommand = z.infer<typeof supportedDynamicLinkingCommandsSchema>

export const isSandboxCommand = (props: bp.HandlerProps): boolean => {
const { req } = props
return extractSandboxCommand(req) !== undefined
}

const NO_MESSAGE_ERROR = { status: 400, body: 'No message found in request' } as const

const extractSandboxCommand = (req: bp.HandlerProps['req']): SupportedDynamicLinkingCommand | undefined => {
const operation = req.headers['x-bp-sandbox-operation']
if (!operation) {
return undefined
}
const operationParseResult = supportedDynamicLinkingCommandsSchema.safeParse(operation)
if (!operationParseResult.success) {
return undefined
}
return operationParseResult.data
}

export const sandboxHandler: bp.IntegrationProps['handler'] = async (props: bp.HandlerProps) => {
const { req } = props
const command = extractSandboxCommand(req)
Expand Down Expand Up @@ -54,11 +43,7 @@ const _handleJoinCommand = async (props: bp.HandlerProps) => {
const whatsapp = await getAuthenticatedWhatsappClient(client, ctx)

await whatsapp.markAsRead(botPhoneNumberId, message.id, 'text')
await whatsapp.sendMessage(
botPhoneNumberId,
userPhoneNumber,
new Text('Conversation connected to bot. You can now send messages. To disconnect, send this message: //leave')
)
await whatsapp.sendMessage(botPhoneNumberId, userPhoneNumber, new Text(CONVERSATION_CONNECTED_MESSAGE))
return
}

Expand All @@ -75,7 +60,7 @@ const _handleLeaveCommand = async (props: bp.HandlerProps) => {
const whatsapp = await getAuthenticatedWhatsappClient(client, ctx)

await whatsapp.markAsRead(botPhoneNumberId, message.id, 'text')
await whatsapp.sendMessage(botPhoneNumberId, userPhoneNumber, new Text('Conversation disconnected from bot'))
await whatsapp.sendMessage(botPhoneNumberId, userPhoneNumber, new Text(CONVERSATION_DISCONNECTED_MESSAGE))
return
}

Expand Down
12 changes: 8 additions & 4 deletions packages/cli/src/command-implementations/build-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ProjectCommand } from './project-command'

export type BuildCommandDefinition = typeof commandDefinitions.build
export class BuildCommand extends ProjectCommand<BuildCommandDefinition> {
public async run(buildContext?: utils.esbuild.IncrementalBuildContext): Promise<void> {
public async run(buildContext?: utils.esbuild.BuildCodeContext): Promise<void> {
const t0 = Date.now()
const { projectType } = this.readProjectDefinitionFromFS()

Expand All @@ -23,10 +23,14 @@ export class BuildCommand extends ProjectCommand<BuildCommandDefinition> {
}

private _runGenerate() {
return new GenerateCommand(this.api, this.prompt, this.logger, this.argv).run()
return new GenerateCommand(this.api, this.prompt, this.logger, this.argv)
.setProjectContext(this.projectContext)
.run()
}

private _runBundle(buildContext?: utils.esbuild.IncrementalBuildContext) {
return new BundleCommand(this.api, this.prompt, this.logger, this.argv).run(buildContext)
private _runBundle(buildContext?: utils.esbuild.BuildCodeContext) {
return new BundleCommand(this.api, this.prompt, this.logger, this.argv)
.setProjectContext(this.projectContext)
.run(buildContext)
}
}
6 changes: 3 additions & 3 deletions packages/cli/src/command-implementations/bundle-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ProjectCommand } from './project-command'

export type BundleCommandDefinition = typeof commandDefinitions.bundle
export class BundleCommand extends ProjectCommand<BundleCommandDefinition> {
public async run(buildContext?: utils.esbuild.IncrementalBuildContext): Promise<void> {
public async run(buildContext?: utils.esbuild.BuildCodeContext): Promise<void> {
const { projectType, resolveProjectDefinition } = this.readProjectDefinitionFromFS()

const abs = this.projectPaths.abs
Expand Down Expand Up @@ -38,11 +38,11 @@ export class BundleCommand extends ProjectCommand<BundleCommandDefinition> {

private async _bundle(
outfile: string,
buildContext?: utils.esbuild.IncrementalBuildContext,
buildContext?: utils.esbuild.BuildCodeContext,
props: Partial<utils.esbuild.BuildOptions> = {}
) {
const abs = this.projectPaths.abs
const context = buildContext ?? new utils.esbuild.IncrementalBuildContext()
const context = buildContext ?? new utils.esbuild.BuildCodeContext()
await context.rebuild(
{
outfile,
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/command-implementations/deploy-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class DeployCommand extends ProjectCommand<DeployCommandDefinition> {
}

private async _runBuild() {
return new BuildCommand(this.api, this.prompt, this.logger, this.argv).run()
return new BuildCommand(this.api, this.prompt, this.logger, this.argv).setProjectContext(this.projectContext).run()
}

private get _visibility(): 'public' | 'private' | 'unlisted' {
Expand Down
8 changes: 5 additions & 3 deletions packages/cli/src/command-implementations/dev-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ export type DevCommandDefinition = typeof commandDefinitions.dev
export class DevCommand extends ProjectCommand<DevCommandDefinition> {
private _initialDef: ProjectDefinition | undefined = undefined
private _cacheDevRequestBody: apiUtils.UpdateBotRequestBody | apiUtils.UpdateIntegrationRequestBody | undefined
private _buildContext: utils.esbuild.IncrementalBuildContext
private _buildContext: utils.esbuild.BuildCodeContext

public constructor(...args: ConstructorParameters<typeof ProjectCommand<DevCommandDefinition>>) {
super(...args)
this._buildContext = new utils.esbuild.IncrementalBuildContext()
this._buildContext = new utils.esbuild.BuildCodeContext()
}

public async run(): Promise<void> {
Expand Down Expand Up @@ -238,7 +238,9 @@ export class DevCommand extends ProjectCommand<DevCommandDefinition> {
}

private _runBuild() {
return new BuildCommand(this.api, this.prompt, this.logger, this.argv).run(this._buildContext)
return new BuildCommand(this.api, this.prompt, this.logger, this.argv)
.setProjectContext(this.projectContext)
.run(this._buildContext)
}

private async _deployDevIntegration(
Expand Down
50 changes: 38 additions & 12 deletions packages/cli/src/command-implementations/project-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,33 @@ class ProjectPaths extends utils.path.PathStore<keyof AllProjectPaths> {
}
}

export class ProjectDefinitionContext {
private _codeCache: Map<string, object> = new Map()
private _buildContext: utils.esbuild.BuildEntrypointContext = new utils.esbuild.BuildEntrypointContext()

public getOrResolveDefinition<T extends object>(code: string): T {
const definition = this._codeCache.get(code)
if (definition) {
return definition as T
}
const result = utils.require.requireJsCode<{ default: object }>(code)
this._codeCache.set(code, result.default)
return result.default as T
}

public rebuildEntrypoint(...args: Parameters<utils.esbuild.BuildEntrypointContext['rebuild']>) {
return this._buildContext.rebuild(...args)
}
}

export abstract class ProjectCommand<C extends ProjectCommandDefinition> extends GlobalCommand<C> {
protected projectContext: ProjectDefinitionContext = new ProjectDefinitionContext()

public setProjectContext(projectContext: ProjectDefinitionContext) {
this.projectContext = projectContext
return this
}

protected override async bootstrap() {
await super.bootstrap()
await this._notifyUpdateSdk()
Expand Down Expand Up @@ -156,17 +182,17 @@ export abstract class ProjectCommand<C extends ProjectCommandDefinition> extends

const bpLintDisabled = await this._isBpLintDisabled(abs.integrationDefinition)

const { outputFiles } = await utils.esbuild.buildEntrypoint({
const { outputFiles } = await this.projectContext.rebuildEntrypoint({
absWorkingDir: abs.workDir,
entrypoint: rel.integrationDefinition,
})

const artifact = outputFiles[0]
const artifact = outputFiles?.[0]
if (!artifact) {
throw new errors.BotpressCLIError('Could not read integration definition')
}

const { default: definition } = utils.require.requireJsCode<{ default: sdk.IntegrationDefinition }>(artifact.text)
const definition = this.projectContext.getOrResolveDefinition<sdk.IntegrationDefinition>(artifact.text)
validateIntegrationDefinition(definition)
return { definition, bpLintDisabled }
}
Expand All @@ -183,17 +209,17 @@ export abstract class ProjectCommand<C extends ProjectCommandDefinition> extends

const bpLintDisabled = await this._isBpLintDisabled(abs.interfaceDefinition)

const { outputFiles } = await utils.esbuild.buildEntrypoint({
const { outputFiles } = await this.projectContext.rebuildEntrypoint({
absWorkingDir: abs.workDir,
entrypoint: rel.interfaceDefinition,
})

const artifact = outputFiles[0]
const artifact = outputFiles?.[0]
if (!artifact) {
throw new errors.BotpressCLIError('Could not read interface definition')
}

const { default: definition } = utils.require.requireJsCode<{ default: sdk.InterfaceDefinition }>(artifact.text)
const definition = this.projectContext.getOrResolveDefinition<sdk.InterfaceDefinition>(artifact.text)

return { definition, bpLintDisabled }
}
Expand All @@ -210,17 +236,17 @@ export abstract class ProjectCommand<C extends ProjectCommandDefinition> extends

const bpLintDisabled = await this._isBpLintDisabled(abs.botDefinition)

const { outputFiles } = await utils.esbuild.buildEntrypoint({
const { outputFiles } = await this.projectContext.rebuildEntrypoint({
absWorkingDir: abs.workDir,
entrypoint: rel.botDefinition,
})

const artifact = outputFiles[0]
const artifact = outputFiles?.[0]
if (!artifact) {
throw new errors.BotpressCLIError('Could not read bot definition')
}

const { default: definition } = utils.require.requireJsCode<{ default: sdk.BotDefinition }>(artifact.text)
const definition = this.projectContext.getOrResolveDefinition<sdk.BotDefinition>(artifact.text)
validateBotDefinition(definition)
return { definition, bpLintDisabled }
}
Expand All @@ -237,17 +263,17 @@ export abstract class ProjectCommand<C extends ProjectCommandDefinition> extends

const bpLintDisabled = await this._isBpLintDisabled(abs.pluginDefinition)

const { outputFiles } = await utils.esbuild.buildEntrypoint({
const { outputFiles } = await this.projectContext.rebuildEntrypoint({
absWorkingDir: abs.workDir,
entrypoint: rel.pluginDefinition,
})

const artifact = outputFiles[0]
const artifact = outputFiles?.[0]
if (!artifact) {
throw new errors.BotpressCLIError('Could not read plugin definition')
}

const { default: definition } = utils.require.requireJsCode<{ default: sdk.PluginDefinition }>(artifact.text)
const definition = this.projectContext.getOrResolveDefinition<sdk.PluginDefinition>(artifact.text)
// TODO: validate plugin definition
return { definition, bpLintDisabled }
}
Expand Down
44 changes: 31 additions & 13 deletions packages/cli/src/utils/esbuild-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,28 @@ const DEFAULT_OPTIONS: esb.BuildOptions = {
minify: false,
}

export class IncrementalBuildContext {
export abstract class BuildContext<T> {
private _context: esb.BuildContext | undefined
private _previousProps: BuildCodeProps | undefined
private _previousProps: T | undefined
private _previousOpts: esb.BuildOptions = {}

private _createContext(props: BuildCodeProps, opts: esb.BuildOptions = {}): Promise<esb.BuildContext> {
protected abstract _createContext(props: T, opts: esb.BuildOptions): Promise<esb.BuildContext>

public async rebuild(props: T, opts: esb.BuildOptions = {}) {
if (!this._context || !_.isEqual(props, this._previousProps) || !_.isEqual(opts, this._previousOpts)) {
if (this._context) {
await this._context.dispose()
}
this._context = await this._createContext(props, opts)
this._previousOpts = opts
this._previousProps = props
}
return await this._context?.rebuild()
}
}

export class BuildCodeContext extends BuildContext<BuildCodeProps> {
protected _createContext(props: BuildCodeProps, opts: esb.BuildOptions = {}): Promise<esb.BuildContext> {
const { absWorkingDir, code, outfile } = props
return esb.context({
...DEFAULT_OPTIONS,
Expand All @@ -44,17 +60,19 @@ export class IncrementalBuildContext {
write: true,
})
}
}

public async rebuild(props: BuildCodeProps, opts: esb.BuildOptions = {}) {
if (!this._context || !_.isEqual(props, this._previousProps) || !_.isEqual(opts, this._previousOpts)) {
if (this._context) {
await this._context.dispose()
}
this._context = await this._createContext(props, opts)
this._previousOpts = opts
this._previousProps = props
}
await this._context?.rebuild()
export class BuildEntrypointContext extends BuildContext<BuildEntrypointProps> {
protected _createContext(props: BuildEntrypointProps, opts: esb.BuildOptions = {}): Promise<esb.BuildContext> {
const { absWorkingDir, entrypoint } = props
return esb.context({
...DEFAULT_OPTIONS,
...opts,
absWorkingDir,
entryPoints: [entrypoint],
outfile: undefined,
write: false,
})
}
}

Expand Down
3 changes: 2 additions & 1 deletion packages/cognitive/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"build:type": "tsup --tsconfig tsconfig.build.json ./src/index.ts --dts-resolve --dts-only --clean",
"build:neutral": "ts-node -T ./build.ts --neutral",
"build": "pnpm build:type && pnpm build:neutral && size-limit",
"test:e2e": "vitest run --dir ./e2e"
"test:e2e": "vitest run --dir ./e2e",
"refresh:models": "ts-node -T ./refresh-models.ts && pnpm prettier src/cognitive-v2/models.ts --write"
},
"size-limit": [
{
Expand Down
Loading
Loading