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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ __snapshots__
.genenv.*
tilt_config.json
/.idea
hubspot.config.yml
15 changes: 0 additions & 15 deletions integrations/hubspot/definitions/actions/contact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ const searchContact: ActionDefinition = {
schema: z.object({
email: z.string().optional().title('Email').describe('The email of the contact to search for'),
phone: z.string().optional().title('Phone').describe('The phone number of the contact to search for'),
properties: z
.array(z.string())
.optional()
.title('Property Names')
.describe('The properties to include in the response'),
}),
},
output: {
Expand Down Expand Up @@ -85,11 +80,6 @@ const getContact: ActionDefinition = {
input: {
schema: z.object({
contactIdOrEmail: z.string().title('Contact ID or Email').describe('The ID or email of the contact to get'),
properties: z
.array(z.string())
.optional()
.title('Properties')
.describe('The properties to include in the response'),
}),
},
output: {
Expand Down Expand Up @@ -151,11 +141,6 @@ const listContacts: ActionDefinition = {
description: 'List contacts in Hubspot',
input: {
schema: z.object({
properties: z
.array(z.string())
.optional()
.title('Properties')
.describe('The properties to include in the response'),
meta: z
.object({
nextToken: z
Expand Down
10 changes: 0 additions & 10 deletions integrations/hubspot/definitions/actions/deal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ const searchDeal: ActionDefinition = {
input: {
schema: z.object({
name: z.string().optional().title('Name').describe('The name of the deal to search for'),
properties: z
.array(z.string())
.optional()
.title('Properties')
.describe('The properties to include in the response'),
}),
},
output: {
Expand Down Expand Up @@ -59,11 +54,6 @@ const getDeal: ActionDefinition = {
input: {
schema: z.object({
dealId: z.string().title('Deal ID').describe('The ID of the deal to get'),
properties: z
.array(z.string())
.optional()
.title('Properties')
.describe('The properties to include in the response'),
}),
},
output: {
Expand Down
10 changes: 0 additions & 10 deletions integrations/hubspot/definitions/actions/lead.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ const searchLead: ActionDefinition = {
input: {
schema: z.object({
name: z.string().optional().title('Name').describe('The name of the lead to search for'),
properties: z
.array(z.string())
.optional()
.title('Properties')
.describe('The properties to include in the response'),
}),
},
output: {
Expand Down Expand Up @@ -62,11 +57,6 @@ const getLead: ActionDefinition = {
input: {
schema: z.object({
leadId: z.string().title('Lead ID').describe('The ID of the lead to get'),
properties: z
.array(z.string())
.optional()
.title('Properties')
.describe('The properties to include in the response'),
}),
},
output: {
Expand Down
2 changes: 1 addition & 1 deletion integrations/hubspot/integration.definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default new IntegrationDefinition({
name: 'hubspot',
title: 'HubSpot',
description: 'Manage contacts, tickets and more from your chatbot.',
version: '3.0.0',
version: '4.0.0',
readme: 'hub.md',
icon: 'icon.svg',
configuration: {
Expand Down
17 changes: 13 additions & 4 deletions integrations/hubspot/src/actions/contact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ const _mapHsContactToBpContact = (hsContact: HsContact): BpContact => ({
updatedAt: hsContact.updatedAt.toISOString(),
})

const _getContactPropertyKeys = async (hsClient: HubspotClient) => {
const properties = await hsClient.getAllObjectProperties('contacts')
return properties.results.map((property) => property.name)
}

export const searchContact: bp.IntegrationProps['actions']['searchContact'] = async ({
client,
ctx,
Expand All @@ -27,16 +32,17 @@ export const searchContact: bp.IntegrationProps['actions']['searchContact'] = as
const phoneStr = input.phone ? `phone ${input.phone}` : 'unknown phone'
const emailStr = input.email ? `email ${input.email}` : 'unknown email'
const infosStr = `${phoneStr} and ${emailStr}`
const propertyKeys = await _getContactPropertyKeys(hsClient)
logger
.forBot()
.debug(
`Searching for contact with ${infosStr} ${input.properties?.length ? `and properties ${input.properties?.join(', ')}` : ''}`
`Searching for contact with ${infosStr} ${propertyKeys?.length ? `and properties ${propertyKeys?.join(', ')}` : ''}`
)

const contact = await hsClient.searchContact({
phone: input.phone,
email: input.email,
propertiesToReturn: input.properties,
propertiesToReturn: propertyKeys,
})

return {
Expand All @@ -63,9 +69,11 @@ export const createContact: bp.IntegrationProps['actions']['createContact'] = as

export const getContact: bp.IntegrationProps['actions']['getContact'] = async ({ ctx, client, input }) => {
const hsClient = await getAuthenticatedHubspotClient({ ctx, client })

const propertyKeys = await _getContactPropertyKeys(hsClient)
const contact = await hsClient.getContact({
contactId: input.contactIdOrEmail,
propertiesToReturn: input.properties,
propertiesToReturn: propertyKeys,
})
return {
contact: _mapHsContactToBpContact(contact),
Expand Down Expand Up @@ -101,8 +109,9 @@ export const deleteContact: bp.IntegrationProps['actions']['deleteContact'] = as

export const listContacts: bp.IntegrationProps['actions']['listContacts'] = async ({ ctx, client, input }) => {
const hsClient = await getAuthenticatedHubspotClient({ ctx, client })
const propertyKeys = await _getContactPropertyKeys(hsClient)
const { contacts, nextToken } = await hsClient.listContacts({
properties: input.properties,
properties: propertyKeys,
nextToken: input.meta.nextToken,
})
return {
Expand Down
11 changes: 9 additions & 2 deletions integrations/hubspot/src/actions/deal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,16 @@ const _mapHsDealToBpDeal = (hsDeal: HsDeal): BpDeal => ({
properties: hsDeal.properties,
})

const _getDealPropertyKeys = async (hsClient: HubspotClient) => {
const properties = await hsClient.getAllObjectProperties('deals')
return properties.results.map((property) => property.name)
}

export const searchDeal: bp.IntegrationProps['actions']['searchDeal'] = async ({ client, ctx, input }) => {
const hsClient = await getAuthenticatedHubspotClient({ client, ctx })
const propertyKeys = await _getDealPropertyKeys(hsClient)

const deal = await hsClient.searchDeal({ name: input.name, propertiesToReturn: input.properties })
const deal = await hsClient.searchDeal({ name: input.name, propertiesToReturn: propertyKeys })

return {
deal: _mapHsDealToBpDeal(deal),
Expand All @@ -40,8 +46,9 @@ export const createDeal: bp.IntegrationProps['actions']['createDeal'] = async ({

export const getDeal: bp.IntegrationProps['actions']['getDeal'] = async ({ client, ctx, input }) => {
const hsClient = await getAuthenticatedHubspotClient({ client, ctx })
const propertyKeys = await _getDealPropertyKeys(hsClient)

const deal = await hsClient.getDealById({ dealId: input.dealId, propertiesToReturn: input.properties })
const deal = await hsClient.getDealById({ dealId: input.dealId, propertiesToReturn: propertyKeys })

return {
deal: _mapHsDealToBpDeal(deal),
Expand Down
11 changes: 9 additions & 2 deletions integrations/hubspot/src/actions/lead.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,16 @@ const _mapHsLeadToBpLead = (hsLead: HsLead): BpLead => ({
properties: hsLead.properties,
})

const _getLeadPropertyKeys = async (hsClient: HubspotClient) => {
const properties = await hsClient.getAllObjectProperties('leads')
return properties.results.map((property) => property.name)
}

export const searchLead: bp.IntegrationProps['actions']['searchLead'] = async ({ client, ctx, input }) => {
const hsClient = await getAuthenticatedHubspotClient({ client, ctx })
const propertyKeys = await _getLeadPropertyKeys(hsClient)

const lead = await hsClient.searchLead({ name: input.name, propertiesToReturn: input.properties })
const lead = await hsClient.searchLead({ name: input.name, propertiesToReturn: propertyKeys })

return {
lead: _mapHsLeadToBpLead(lead),
Expand All @@ -41,8 +47,9 @@ export const createLead: bp.IntegrationProps['actions']['createLead'] = async ({

export const getLead: bp.IntegrationProps['actions']['getLead'] = async ({ client, ctx, input }) => {
const hsClient = await getAuthenticatedHubspotClient({ client, ctx })
const propertyKeys = await _getLeadPropertyKeys(hsClient)

const lead = await hsClient.getLeadById({ leadId: input.leadId, propertiesToReturn: input.properties })
const lead = await hsClient.getLeadById({ leadId: input.leadId, propertiesToReturn: propertyKeys })

return {
lead: _mapHsLeadToBpLead(lead),
Expand Down
11 changes: 11 additions & 0 deletions integrations/hubspot/src/hubspot-api/hubspot-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,17 @@ export class HubspotClient {
return newContact
}

/** Gets the list of properties for a given object type.
*
* Object type examples:
* - 'contacts'
* - 'deals'
* - 'leads'
*/
public getAllObjectProperties(objectType: string) {
return this._hsClient.crm.properties.coreApi.getAll(objectType)
}

@handleErrors('Failed to get contact by ID')
public async getContact({ contactId, propertiesToReturn }: { contactId: string; propertiesToReturn?: string[] }) {
const allPropertiesToReturn = [...DEFAULT_CONTACT_PROPERTIES, ...(propertiesToReturn ?? [])]
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@botpress/cli",
"version": "4.17.15",
"version": "4.17.16",
"description": "Botpress CLI",
"scripts": {
"build": "pnpm run build:types && pnpm run bundle && pnpm run template:gen",
Expand Down
13 changes: 10 additions & 3 deletions packages/cli/src/command-implementations/add-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import * as errors from '../errors'
import * as pkgRef from '../package-ref'
import * as utils from '../utils'
import { GlobalCommand } from './global-command'
import { ProjectCache, ProjectCommand, ProjectCommandDefinition, ProjectDefinition } from './project-command'
import {
ProjectCache,
ProjectCommand,
ProjectCommandDefinition,
ProjectDefinitionLazy,
ProjectDefinition,
} from './project-command'

type InstallablePackage =
| {
Expand Down Expand Up @@ -291,7 +297,8 @@ export class AddCommand extends GlobalCommand<AddCommandDefinition> {
}> {
const cmd = this._getProjectCmd(workDir)

const definition = await cmd.readProjectDefinitionFromFS().catch((thrown) => {
const { resolveProjectDefinition } = cmd.readProjectDefinitionFromFS()
const definition = await resolveProjectDefinition().catch((thrown) => {
if (thrown instanceof errors.ProjectDefinitionNotFoundError) {
return undefined
}
Expand Down Expand Up @@ -334,7 +341,7 @@ class _AnyProjectCommand extends ProjectCommand<ProjectCommandDefinition> {
throw new errors.BotpressCLIError('Not implemented')
}

public async readProjectDefinitionFromFS(): Promise<ProjectDefinition> {
public readProjectDefinitionFromFS(): ProjectDefinitionLazy {
return super.readProjectDefinitionFromFS()
}

Expand Down
6 changes: 2 additions & 4 deletions packages/cli/src/command-implementations/build-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@ export type BuildCommandDefinition = typeof commandDefinitions.build
export class BuildCommand extends ProjectCommand<BuildCommandDefinition> {
public async run(buildContext?: utils.esbuild.IncrementalBuildContext): Promise<void> {
const t0 = Date.now()
const { type: projectType, definition: integrationDef } = await this.readProjectDefinitionFromFS()
const { projectType } = this.readProjectDefinitionFromFS()

if (projectType === 'interface') {
this.logger.success('Interface projects have nothing to build.')
return
}

if (integrationDef) {
await this._runGenerate()
}
await this._runGenerate()

await this._runBundle(buildContext)
const dt = Date.now() - t0
Expand Down
12 changes: 6 additions & 6 deletions packages/cli/src/command-implementations/bundle-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,29 @@ 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> {
const projectDef = await this.readProjectDefinitionFromFS()
const { projectType, resolveProjectDefinition } = this.readProjectDefinitionFromFS()

const abs = this.projectPaths.abs
const rel = this.projectPaths.rel('workDir')
const line = this.logger.line()

if (projectDef.type === 'interface') {
if (projectType === 'interface') {
this.logger.success('Interface projects have no implementation to bundle.')
} else if (projectDef.type === 'integration') {
} else if (projectType === 'integration') {
const projectDef = await resolveProjectDefinition()
const { name, __advanced } = projectDef.definition
line.started(`Bundling integration ${chalk.bold(name)}...`)
await this._bundle(abs.outFileCJS, buildContext, __advanced?.esbuild ?? {})
} else if (projectDef.type === 'bot') {
} else if (projectType === 'bot') {
line.started('Bundling bot...')
await this._bundle(abs.outFileCJS, buildContext)
} else if (projectDef.type === 'plugin') {
} else if (projectType === 'plugin') {
line.started('Bundling plugin with platform node...')
await this._bundle(abs.outFileCJS, buildContext)

line.started('Bundling plugin with platform browser...')
await this._bundle(abs.outFileESM, buildContext, { platform: 'browser', format: 'esm' })
} else {
type _assertion = utils.types.AssertNever<typeof projectDef>
throw new errors.UnsupportedProjectType()
}

Expand Down
14 changes: 9 additions & 5 deletions packages/cli/src/command-implementations/deploy-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,22 @@ export class DeployCommand extends ProjectCommand<DeployCommandDefinition> {
await this._runBuild() // This ensures the bundle is always synced with source code
}

const projectDef = await this.readProjectDefinitionFromFS()
const { projectType, resolveProjectDefinition } = this.readProjectDefinitionFromFS()

if (projectDef.type === 'integration') {
if (projectType === 'integration') {
const projectDef = await resolveProjectDefinition()
return this._deployIntegration(api, projectDef.definition)
}
if (projectDef.type === 'interface') {
if (projectType === 'interface') {
const projectDef = await resolveProjectDefinition()
return this._deployInterface(api, projectDef.definition)
}
if (projectDef.type === 'plugin') {
if (projectType === 'plugin') {
const projectDef = await resolveProjectDefinition()
return this._deployPlugin(api, projectDef.definition)
}
if (projectDef.type === 'bot') {
if (projectType === 'bot') {
const projectDef = await resolveProjectDefinition()
return this._deployBot(api, projectDef.definition, this.argv.botId, this.argv.createNewBot)
}
throw new errors.UnsupportedProjectType()
Expand Down
15 changes: 9 additions & 6 deletions packages/cli/src/command-implementations/dev-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ export class DevCommand extends ProjectCommand<DevCommandDefinition> {

const api = await this.ensureLoginAndCreateClient(this.argv)

const projectDef = await this.readProjectDefinitionFromFS()
if (projectDef.type === 'interface') {
const { projectType, resolveProjectDefinition } = this.readProjectDefinitionFromFS()
if (projectType === 'interface') {
throw new errors.BotpressCLIError('This feature is not available for interfaces.')
}
const projectDef = await resolveProjectDefinition()
this._initialDef = projectDef

let env: Record<string, string> = {
Expand Down Expand Up @@ -189,16 +190,18 @@ export class DevCommand extends ProjectCommand<DevCommandDefinition> {
}

private _deploy = async (api: apiUtils.ApiClient, tunnelUrl: string) => {
const projectDef = await this.readProjectDefinitionFromFS()
const { projectType, resolveProjectDefinition } = this.readProjectDefinitionFromFS()

if (projectDef.type === 'interface') {
if (projectType === 'interface') {
throw new errors.BotpressCLIError('This feature is not available for interfaces.')
}
if (projectDef.type === 'integration') {
if (projectType === 'integration') {
const projectDef = await resolveProjectDefinition()
this._checkSecrets(projectDef.definition)
return await this._deployDevIntegration(api, tunnelUrl, projectDef.definition)
}
if (projectDef.type === 'bot') {
if (projectType === 'bot') {
const projectDef = await resolveProjectDefinition()
return await this._deployDevBot(api, tunnelUrl, projectDef.definition)
}
throw new errors.UnsupportedProjectType()
Expand Down
Loading
Loading