Skip to content

Commit

Permalink
Merge pull request #76 from SAPConversationalAI/mergeToOpenSource
Browse files Browse the repository at this point in the history
Merge to open source
  • Loading branch information
JWandrocke authored Aug 24, 2021
2 parents 9480199 + 0d6f221 commit e0618ab
Show file tree
Hide file tree
Showing 15 changed files with 24,271 additions and 14,317 deletions.
2 changes: 1 addition & 1 deletion .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[
"@babel/preset-env", {
"targets": {
"browsers": [">1%", "not op_mini all"],
"browsers": [">1%", "not op_mini all", "ie 11"],
"node": "current"
},
"useBuiltIns": "usage",
Expand Down
38,457 changes: 24,165 additions & 14,292 deletions package-lock.json

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "webchat",
"version": "1.4.35",
"version": "1.4.37",
"description": "",
"main": "lib/index.js",
"scripts": {
Expand All @@ -17,16 +17,16 @@
"test": "MOCHA_TEST=true nyc --reporter=text mocha 'src/**/*.test.js'",
"testHtml": "MOCHA_TEST=true nyc --reporter=html --report-dir coverage/html mocha --reporter mocha-junit-reporter --reporter-options mochaFile=./reports/mocha.xml 'src/**/*.test.js'",
"coverage": "nyc report --cache false --reporter=html --report-dir coverage/html",
"coverage:clover": "nyc report --cache false --reporter=clover --report-dir coverage/clover && chown -R 1000:999 coverage/clover",
"coverage:cobertura": "nyc report --cache false --reporter=cobertura --report-dir coverage/cobertura && chown -R 1000:999 coverage/cobertura",
"coverage:html": "nyc report --cache false --reporter=html --report-dir coverage/html && chown -R 1000:999 coverage/html",
"coverage:lcov": "nyc report --cache false --reporter=lcov --report-dir coverage/lcov && chown -R 1000:999 coverage/lcov"
"coverage:clover": "nyc report --cache false --reporter=clover --report-dir coverage/clover",
"coverage:cobertura": "nyc report --cache false --reporter=cobertura --report-dir coverage/cobertura",
"coverage:html": "nyc report --cache false --reporter=html --report-dir coverage/html",
"coverage:lcov": "nyc report --cache false --reporter=lcov --report-dir coverage/lcov"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"@braintree/sanitize-url": "^2.1.0",
"@braintree/sanitize-url": "^5.0.2",
"@types/react": "16.8.22",
"axios": "^0.21.1",
"classnames": "^2.2.5",
Expand Down Expand Up @@ -59,6 +59,7 @@
"@babel/preset-react": "^7.0.0",
"@babel/register": "^7.6.0",
"@babel/runtime": "7.12.1",
"@ui5/cli": "^2.0.2",
"autoprefixer": "^9.6.1",
"axios-mock-adapter": "^1.18.2",
"babel-eslint": "^10.0.3",
Expand All @@ -78,7 +79,6 @@
"eslint-config-zavatta-react": "^2.3.1",
"eslint-plugin-react": "^7.14.3",
"esm": "^3.2.25",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.5",
"ignore-styles": "^5.0.1",
"jsdom": "^16.4.0",
Expand All @@ -99,7 +99,7 @@
"sass-loader": "^7.3.1",
"sinon": "^9.2.4",
"style-loader": "^1.0.0",
"uglifyjs-webpack-plugin": "^1.1.2",
"uglifyjs-webpack-plugin": "2.2.0",
"webpack": "^4.41.2",
"webpack-dev-server": "^3.9.0"
},
Expand Down
5 changes: 5 additions & 0 deletions src/components/Button/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

font-weight: bold;
color: cornflowerblue;

overflow: hidden;
word-wrap: break-word;
word-break: break-word;
overflow-wrap: break-word;

&--ReadOnly {
opacity: 0.5;
Expand Down
1 change: 0 additions & 1 deletion src/components/Live/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ class Live extends Component {
lastMessage
&& (sendMessagePromiseCondition || pollMessageCondition)
&& !lastMessage.retry
&& !lastMessage.isSending
&& showTyping
)

Expand Down
11 changes: 9 additions & 2 deletions src/components/Message/Card.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,20 @@ import { sanitizeUrl } from '@braintree/sanitize-url'
import { truncate, safeArrayOfItem } from 'helpers'

import Button from 'components/Button'
import { propOr } from 'ramda'

const Card = ({ content, sendMessage, onImageLoaded, readOnlyMode, isLastMessage }) => {
const { title, subtitle, imageUrl, buttons } = content
const title = propOr('', 'title', content)
const subtitle = propOr(null, 'subtitle', content)
const buttons = propOr(null, 'buttons', content)
let imageUrl = propOr(null, 'imageUrl', content)

if (imageUrl && sanitizeUrl(imageUrl) === 'about:blank') {
return null
console.warn('Warning the image url is not supported')
// If the image is invalid still show the card without the image
imageUrl = null
}

// https://sapjira.wdf.sap.corp/browse/SAPMLCONV-6296
// Need to check if buttons is null before rendering the button html.
return (
Expand Down
14 changes: 12 additions & 2 deletions src/components/Message/Text.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ const allowedMarkdownTypes = [
'tableCell',
]

// Export for unit test
export const getValidMarkDownLinkString = (isMarkdown, compiledResponse) => {
if (isMarkdown && compiledResponse) {
// Search the text starting with [ with :// and ends with ]
return compiledResponse
}

return null
}

const Text = ({ content, style, isMarkdown, readOnlyMode }) => {
const respond = safeStringValue(content)

Expand Down Expand Up @@ -66,15 +76,15 @@ const Text = ({ content, style, isMarkdown, readOnlyMode }) => {
rel='noopener noreferrer'>{props.children}
</a>)
}
const markDownResponse = getValidMarkDownLinkString(isMarkdown, compiledResponse)

return (
<div style={style} className={'RecastAppText CaiAppText'}>
{isMarkdown ? (
<ReactMarkdown
plugins={[gfm]}
renderers={{ link: LinkRenderer }}
allowedTypes={allowedMarkdownTypes}
>{compiledResponse}
allowedTypes={allowedMarkdownTypes}>{markDownResponse}
</ReactMarkdown>
) : (
compiledResponse
Expand Down
10 changes: 7 additions & 3 deletions src/components/Message/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,8 @@

.RecastAppPicture, .CaiAppPicture {
width: 270px;
max-height: 270px;

max-height: 540px;
object-fit: cover;
border-radius: 3px;
border: 1px solid lightgrey;
}
Expand Down Expand Up @@ -352,7 +352,11 @@

&--text {
padding: 0.4rem;

overflow: hidden;
word-wrap: break-word;
word-break: break-word;
overflow-wrap: break-word;

&-title {
font-weight: bold;
}
Expand Down
4 changes: 3 additions & 1 deletion src/containers/Chat/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,9 @@ class Chat extends Component {
conversationId,
lastMessageId,
)
shouldPoll = waitTime === 0
// Handle the case where waitTime is null
const waitingTime = typeof waitTime === 'number' ? waitTime : 0
shouldPoll = waitingTime === 0
shouldWaitXseconds = waitTime > 0
numberCallsWithoutAnyMessages = this._deteremNumberCallsWithoutAnyMessages(numberCallsWithoutAnyMessages, shouldWaitXseconds, messages)
timeToSleep = waitTime * 1000
Expand Down
39 changes: 38 additions & 1 deletion src/middlewares/api.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,36 @@
import config from 'config'
import qs from 'query-string'
import axios from 'axios'
import { pathOr, propOr } from 'ramda'

let lastProcessed = null
let createdConversation = false

function waitforTime (milisec) {
return new Promise(resolve => { setTimeout(resolve, milisec) })
}

const delayBetweenMessages = async (messages, dispatch) => {
const lastMsg = messages.slice(-1)[0]
const lastMessageId = propOr(null, 'id', lastMsg)
if (lastMessageId) {
if (lastProcessed === lastMessageId) {
// already processed skip for now. (Bug in server return the same list over and over)
return
}
lastProcessed = lastMessageId
// For polling set the lastMessageId to avoid getting the same messages again while dispatching
dispatch({ type: 'SET_CREDENTIALS', payload: { lastMessageId } })
}
for (const msg of messages) {
dispatch({ type: 'ADD_MESSAGES', payload: { messages: [msg] } })
if (lastMessageId !== msg.id) {
// If there is a delay in this message wait before showing the next message.
const messageDelay = pathOr(0, ['attachment', 'delay'], msg) * 1000
await waitforTime(messageDelay)
}
}
}

export default store => next => action => {
if (!action.type.startsWith('API:')) {
Expand All @@ -24,7 +54,14 @@ export default store => next => action => {

return axios(options)
.then(res => {
dispatch({ type: `${prefix}_SUCCESS`, payload: { ...res.data.results } })
createdConversation = createdConversation || prefix === 'CREATE_CONVERSATION'
const isFirstCall = propOr(null, 'last_message_id', query) === null && !createdConversation
if (prefix === 'POLL_MESSAGES' && !isFirstCall) {
const messages = pathOr([], ['data', 'results', 'messages'], res)
delayBetweenMessages(messages, dispatch)
} else {
dispatch({ type: `${prefix}_SUCCESS`, payload: { ...res.data.results } })
}
return res.data.results
})
.catch(err => {
Expand Down
11 changes: 10 additions & 1 deletion src/reducers/messages.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { handleActions } from 'redux-actions'
import uniqWith from 'ramda/es/uniqWith'
import propOr from 'ramda/es/propOr'
import { ascend, prop, sortWith } from 'ramda'

const initialState = []

Expand All @@ -24,6 +25,14 @@ export default handleActions(
return uniqWith((m1, m2) => m1.id === m2.id, [...state, ...payload.messages])
},

ADD_MESSAGES: (state, { payload }) => {
// Avoid adding duplicate messages
const uniqMessageList = uniqWith((m1, m2) => m1.id === m2.id, [...state, ...payload.messages])
// Handle the case where the messages are dispatched out of order,
// make sure the list is sorted by the received at date
return sortWith([ascend(prop('receivedAt'))], uniqMessageList)
},

GET_MESSAGES_SUCCESS: (state, { payload: messages }) => {
return messages
},
Expand All @@ -41,7 +50,7 @@ export default handleActions(
conversationExpired: status === 404
&& typeof errorMessage === 'string'
&& errorMessage.includes('Conversation not found'),
id: `local-${Math.random()}`,
id: `local-${Math.random()}`,
participant: {
isBot: false,
},
Expand Down
3 changes: 2 additions & 1 deletion src/test/components/Button/Button.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ describe('<Button>', () => {
wrapper.unmount()
})
it('handle render invalid url', () => {
const wrapper = mount(createButton({ value: 'Testing', title: 'Url', type: 'web_url' }, false))
// eslint-disable-next-line no-script-url
const wrapper = mount(createButton({ value: 'javascript:alert(document.domain)', title: 'Url', type: 'web_url' }, false))
expect(wrapper.find('a').exists()).to.equal(false)
wrapper.unmount()
})
Expand Down
3 changes: 2 additions & 1 deletion src/test/components/Message/Card.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ describe('<Card>', () => {
const wrapper = mount(
<Card
isLastMessage
content={{ title: 'Title', imageUrl: 'badurl' }}
// eslint-disable-next-line no-script-url
content={{ title: 'Title', imageUrl: 'javascript:alert(document.domain)' }}
preferences={preferences}
onClick={() => { /* */ }} />
)
Expand Down
3 changes: 2 additions & 1 deletion src/test/components/Message/Picture.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ describe('<Picture>', () => {
const wrapper = mount(
<Picture
isLastMessage
content={'badimage'}
// eslint-disable-next-line no-script-url
content={'javascript:alert(document.domain)'}
preferences={preferences} />
)
expect(wrapper.find('Picture').exists()).to.equal(true)
Expand Down
9 changes: 7 additions & 2 deletions src/test/components/Message/Text.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react'
import { mount } from 'enzyme'
import { expect } from 'chai'
import { assert, expect } from 'chai'

import { preferences } from 'test/preferenceUtil'
import Text from 'components/Message/Text'
import Text, { getValidMarkDownLinkString } from 'components/Message/Text'

describe('<Text>', () => {
it('should render MarkDown', () => {
Expand Down Expand Up @@ -41,5 +41,10 @@ describe('<Text>', () => {
expect(anchor.prop('href')).to.equal('#')
wrapper.unmount()
})
it('Markdown link validation test', () => {
assert.isNull(getValidMarkDownLinkString(false, 'Testing text'), 'Not a markdown')
assert.isNull(getValidMarkDownLinkString(true), 'Missing markdown text')
expect(getValidMarkDownLinkString(true, '[Testing text]'), 'Text markdown').to.equal('[Testing text]')
})
})

0 comments on commit e0618ab

Please sign in to comment.