Skip to content

Commit

Permalink
Merge pull request #1152 from botpress/ya-state-fix
Browse files Browse the repository at this point in the history
fix state, changed how elements are sent and fixed content types
  • Loading branch information
allardy authored Nov 21, 2018
2 parents 7690da9 + 6ecc36a commit f9105ff
Show file tree
Hide file tree
Showing 19 changed files with 131 additions and 144 deletions.
3 changes: 3 additions & 0 deletions modules/basic-skills/src/actions/choice_invalid_answer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
'use strict'
const _ = require('lodash')

/**
* @hidden true
*/
const invalidAnswer = async () => {
const key = 'skill-choice-invalid-count'
const value = (state[key] || 0) + 1
Expand Down
1 change: 1 addition & 0 deletions modules/basic-skills/src/actions/choice_parse_answer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const INTENT_PREFIX = 'intent:'
* Get a variable under this user's storage
* @title Validate user choice
* @category Skills
* @hidden true
* @author Botpress, Inc.
* @param {string} data - The parameters of the available choices
*/
Expand Down
4 changes: 3 additions & 1 deletion modules/qna/src/views/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ export default class QnaAdmin extends Component {

fetchFlows() {
this.props.bp.axios.get('/flows').then(({ data }) => {
const flowsList = data.map(({ name }) => ({ label: name, value: name }))
const flowsList = data
.filter(flow => !flow.name.startsWith('skills/'))
.map(({ name }) => ({ label: name, value: name }))

this.setState({ flows: data, flowsList })
})
Expand Down
12 changes: 6 additions & 6 deletions src/bp/core/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,16 @@ const event = (eventEngine: EventEngine): typeof sdk.events => {
sendEvent(event: sdk.IO.Event): void {
eventEngine.sendEvent(event)
},
replyToEvent(event: sdk.IO.Event, payloads: any[]): void {
eventEngine.replyToEvent(event, payloads)
replyToEvent(eventDestination: sdk.IO.EventDestination, payloads: any[]): void {
eventEngine.replyToEvent(eventDestination, payloads)
}
}
}

const dialog = (dialogEngine: DialogEngine, sessionRepo: SessionRepository): typeof sdk.dialog => {
return {
async createId(event: sdk.IO.Event) {
return SessionIdFactory.createIdFromEvent(event)
createId(eventDestination: sdk.IO.EventDestination) {
return SessionIdFactory.createIdFromEvent(eventDestination)
},
async processEvent(sessionId: string, event: sdk.IO.IncomingEvent): Promise<void> {
await dialogEngine.processEvent(sessionId, event)
Expand Down Expand Up @@ -164,8 +164,8 @@ const cms = (cmsService: CMSService): typeof sdk.cms => {
getAllContentTypes(botId?: string): Promise<any[]> {
return cmsService.getAllContentTypes(botId)
},
renderElement(contentTypeId: string, payload: any, channel: string): Promise<any> {
return cmsService.renderElement(contentTypeId, payload, channel)
renderElement(contentId: string, args: any, eventDestination: sdk.IO.EventDestination): Promise<any> {
return cmsService.renderElement(contentId, args, eventDestination)
},
createOrUpdateContentElement(
botId: string,
Expand Down
8 changes: 8 additions & 0 deletions src/bp/core/services/action/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import doctrine from 'doctrine'
import { meta } from 'joi'
import _ from 'lodash'
import yn from 'yn'

// Credit: https://stackoverflow.com/questions/35905181/regex-for-jsdoc-comments
const JSDocCommentRegex = /\/\*\*\s*\n([^\*]|(\*(?!\/)))*\*\//gi
Expand All @@ -10,6 +11,7 @@ export type ActionMetadata = {
category: string
description: string
author: string
hidden: boolean
params: {
type: string
required: boolean
Expand All @@ -25,6 +27,7 @@ export const extractMetadata = (code: string) => {
category: '',
description: '',
author: '',
hidden: false,
params: []
}

Expand Down Expand Up @@ -57,6 +60,11 @@ export const extractMetadata = (code: string) => {
metadata.title = (title as any).description || ''
}

const hidden = _.find(extracted.tags, { title: 'hidden' })
if (hidden) {
metadata.hidden = yn((hidden as any).description)
}

metadata.params = _.filter(extracted.tags, { title: 'param' }).map(tag => {
const type: string = _.get(tag, 'type.name', '')
const required = _.get(tag, 'type.type') !== doctrine.type.Syntax.OptionalType
Expand Down
47 changes: 36 additions & 11 deletions src/bp/core/services/cms/cms-service.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Logger } from 'botpress/sdk'
import { IO, Logger } from 'botpress/sdk'
import { ContentElement, ContentType, SearchParams } from 'botpress/sdk'
import { KnexExtension } from 'common/knex'
import { inject, injectable, tagged } from 'inversify'
import Knex from 'knex'
import _ from 'lodash'
import Mustache from 'mustache'
import nanoid from 'nanoid'
import path from 'path'
import plur from 'plur'
Expand Down Expand Up @@ -403,19 +404,43 @@ export class CMSService implements IDisposeOnExit {
return !contentType.computePreviewText ? 'No preview' : contentType.computePreviewText(formData)
}

private computeData(contentTypeId, formData) {
const contentType = this.contentTypes.find(x => x.id === contentTypeId)
if (!contentType) {
throw new Error(`Unknown content type ${contentTypeId}`)
}
async renderElement(contentId, args, eventDestination: IO.EventDestination) {
const { botId, channel } = eventDestination
let contentType = contentId
contentId = contentId.replace(/^#?/i, '')

return !contentType.computeData ? formData : contentType.computeData(contentTypeId, formData)
}
if (contentId.startsWith('!')) {
const content = await this.getContentElement(botId, contentId.substr(1)) // TODO handle errors

if (!content) {
throw new Error(`Content element "${contentId}" not found`)
}

_.set(content, 'previewPath', Mustache.render(content.previewText, args))

async renderElement(contentTypeId, payload, channel) {
const contentType = await this.getContentType(contentTypeId)
const text = _.get(content.formData, 'text')
const variations = _.get(content.formData, 'variations')

const message = _.sample([text, ...(variations || [])])
if (message) {
_.set(content, 'formData.text', Mustache.render(message, args))
}

contentType = content.contentType
args = {
...args,
...content.formData
}
}

const contentTypeRenderer = await this.getContentType(contentType)
const additionnalData = { BOT_URL: process.EXTERNAL_URL }

return await contentType.renderElement({ ...additionnalData, ...payload }, channel)
let payloads = await contentTypeRenderer.renderElement({ ...additionnalData, ...args }, channel)
if (!_.isArray(payloads)) {
payloads = [payloads]
}

return payloads
}
}
69 changes: 0 additions & 69 deletions src/bp/core/services/cms/content-sender.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/bp/core/services/dialog/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export class DialogEngine {
...context,
currentNode: parentNode.name,
currentFlow: parentFlow.name,
queue: queue.toString()
queue
}
this._logExitFlow(event.botId, context.currentFlow, context.currentFlow, parentFlow.name, parentNode.name)
} else if (transitionTo === 'END') {
Expand Down
16 changes: 13 additions & 3 deletions src/bp/core/services/dialog/instruction/strategy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { IO, Logger } from 'botpress/sdk'
import { CMSService } from 'core/services/cms/cms-service'
import { EventEngine } from 'core/services/middleware/event-engine'
import { inject, injectable, tagged } from 'inversify'
import _ from 'lodash'
import Mustache from 'mustache'
Expand All @@ -8,7 +10,6 @@ import { container } from '../../../app.inversify'
import { TYPES } from '../../../types'
import ActionService from '../../action/action-service'
import { VmRunner } from '../../action/vm'
import { ContentElementSender } from '../../cms/content-sender'

import { Instruction, InstructionType, ProcessingResult } from '.'

Expand Down Expand Up @@ -37,7 +38,8 @@ export class ActionStrategy implements InstructionStrategy {
@tagged('name', 'Actions')
private logger: Logger,
@inject(TYPES.ActionService) private actionService: ActionService,
@inject(TYPES.ContentElementSender) private contentElementSender: ContentElementSender
@inject(TYPES.EventEngine) private eventEngine: EventEngine,
@inject(TYPES.CMSService) private cmsService: CMSService
) {}

async processInstruction(botId, instruction, event): Promise<ProcessingResult> {
Expand Down Expand Up @@ -76,7 +78,15 @@ export class ActionStrategy implements InstructionStrategy {

event.state.session.lastMessages.push(message)

await this.contentElementSender.sendContent(outputType, args, event)
args = {
...args,
state: _.get(event, 'state.context.data') || {},
user: _.get(event, 'state.user') || {}
}

const eventDestination = _.pick(event, ['channel', 'target', 'botId', 'threadId'])
const renderedElements = await this.cmsService.renderElement(outputType, args, eventDestination)
await this.eventEngine.replyToEvent(eventDestination, renderedElements)

return ProcessingResult.none()
}
Expand Down
5 changes: 3 additions & 2 deletions src/bp/core/services/dialog/session/id-factory.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { IO } from 'botpress/sdk'

export class SessionIdFactory {
static createIdFromEvent(event: IO.Event) {
return `${event.channel}::${event.target}::${event.threadId}`
static createIdFromEvent(eventDestination: IO.EventDestination) {
const { channel, target, threadId } = eventDestination
return `${channel}::${target}::${threadId}`
}

static createTargetFromId(sessionId: string) {
Expand Down
22 changes: 11 additions & 11 deletions src/bp/core/services/hook/hook-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export namespace Hooks {
}

class HookScript {
constructor(public hook: Hooks.BaseHook, public path: string, public filename: string, public code: string) {}
constructor(public path: string, public filename: string, public code: string) {}
}

@injectable()
Expand Down Expand Up @@ -116,7 +116,7 @@ export class HookService {

async executeHook(hook: Hooks.BaseHook): Promise<void> {
const scripts = await this.extractScripts(hook)
await Promise.mapSeries(_.orderBy(scripts, ['filename'], ['asc']), script => this.runScript(script))
await Promise.mapSeries(_.orderBy(scripts, ['filename'], ['asc']), script => this.runScript(script, hook))
}

private async extractScripts(hook: Hooks.BaseHook): Promise<HookScript[]> {
Expand All @@ -130,7 +130,7 @@ export class HookService {
const scripts = await Promise.map(filesPaths, async path => {
const script = await this.ghost.global().readFileAsString('hooks/' + hook.folder, path)
const filename = path.replace(/^.*[\\\/]/, '')
return new HookScript(hook, path, filename, script)
return new HookScript(path, filename, script)
})

this._scriptsCache.set(hook.folder, scripts)
Expand All @@ -155,11 +155,11 @@ export class HookService {
return module => requireAtPaths(module, lookups)
}

private async runScript(hookScript: HookScript) {
const hookPath = `/data/global/hooks/${hookScript.hook.folder}/${hookScript.path}.js`
private async runScript(hookScript: HookScript, hook: Hooks.BaseHook) {
const hookPath = `/data/global/hooks/${hook.folder}/${hookScript.path}.js`
const dirPath = path.resolve(path.join(process.PROJECT_LOCATION, hookPath))

const _require = this._prepareRequire(path.dirname(dirPath), hookScript.hook.folder)
const _require = this._prepareRequire(path.dirname(dirPath), hook.folder)

const modRequire = new Proxy(
{},
Expand All @@ -172,23 +172,23 @@ export class HookService {
wrapper: 'none',
console: 'inherit',
sandbox: {
...hookScript.hook.args,
...hook.args,
process: _.pick(process, 'HOST', 'PORT', 'EXTERNAL_URL', 'env')
},
timeout: hookScript.hook.timeout,
timeout: hook.timeout,
require: {
external: true,
mock: modRequire
}
})

const botId = _.get(hookScript.hook.args, 'event.botId')
const botId = _.get(hook.args, 'event.botId')
const vmRunner = new VmRunner()

await vmRunner.runInVm(vm, hookScript.code, hookScript.path).catch(err => {
this.logScriptError(err, botId, hookScript.path, hookScript.hook.folder)
this.logScriptError(err, botId, hookScript.path, hook.folder)
})
this.logScriptRun(botId, hookScript.path, hookScript.hook.folder)
this.logScriptRun(botId, hookScript.path, hook.folder)
}

private logScriptError(err, botId, path, folder) {
Expand Down
6 changes: 2 additions & 4 deletions src/bp/core/services/middleware/event-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,10 @@ export class EventEngine {
}
}

async replyToEvent(event: sdk.IO.Event, payloads: any[]) {
const originalEvent = _.pick(event, 'channel', 'target', 'botId', 'threadId')

async replyToEvent(eventDestination: sdk.IO.EventDestination, payloads: any[]) {
for (const payload of payloads) {
const replyEvent = Event({
...originalEvent,
...eventDestination,
direction: 'outgoing',
type: _.get(payload, 'type', 'default'),
payload
Expand Down
Loading

0 comments on commit f9105ff

Please sign in to comment.