Skip to content

Commit

Permalink
feat: allow adding custom attributes to conversations from the SDK (#…
Browse files Browse the repository at this point in the history
…6782)

* feat: add conversation attributes method to sdk and widget app

* feat: add endpoints to update custom attributes

* refactor: update SDK api

* feat: add api and actions for conversation updates

* fix: error message

* test: custom attributes on conversations controller

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
  • Loading branch information
scmmishra and muhsin-k committed Apr 4, 2023
1 parent 6a0ca35 commit a040aee
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 0 deletions.
10 changes: 10 additions & 0 deletions app/controllers/api/v1/widget/conversations_controller.rb
Expand Up @@ -65,6 +65,16 @@ def toggle_status
head :ok
end

def set_custom_attributes
conversation.update!(custom_attributes: permitted_params[:custom_attributes])
end

def destroy_custom_attributes
conversation.custom_attributes = conversation.custom_attributes.excluding(params[:custom_attribute])
conversation.save!
render json: conversation
end

private

def trigger_typing_event(event)
Expand Down
20 changes: 20 additions & 0 deletions app/javascript/packs/sdk.js
Expand Up @@ -114,6 +114,26 @@ const runSDK = ({ baseUrl, websiteToken }) => {
}
},

setConversationCustomAttributes(customAttributes = {}) {
if (!customAttributes || !Object.keys(customAttributes).length) {
throw new Error('Custom attributes should have atleast one key');
} else {
IFrameHelper.sendMessage('set-conversation-custom-attributes', {
customAttributes,
});
}
},

deleteConversationCustomAttribute(customAttribute = '') {
if (!customAttribute) {
throw new Error('Custom attribute is required');
} else {
IFrameHelper.sendMessage('delete-conversation-custom-attribute', {
customAttribute,
});
}
},

setLabel(label = '') {
IFrameHelper.sendMessage('set-label', { label });
},
Expand Down
10 changes: 10 additions & 0 deletions app/javascript/widget/App.vue
Expand Up @@ -274,6 +274,16 @@ export default {
'contacts/deleteCustomAttribute',
message.customAttribute
);
} else if (message.event === 'set-conversation-custom-attributes') {
this.$store.dispatch(
'conversation/setCustomAttributes',
message.customAttributes
);
} else if (message.event === 'delete-conversation-custom-attribute') {
this.$store.dispatch(
'conversation/deleteCustomAttribute',
message.customAttribute
);
} else if (message.event === 'set-locale') {
this.setLocale(message.locale);
this.setBubbleLabel();
Expand Down
20 changes: 20 additions & 0 deletions app/javascript/widget/api/conversation.js
Expand Up @@ -50,6 +50,24 @@ const toggleStatus = async () => {
);
};

const setCustomAttributes = async customAttributes => {
return API.post(
`/api/v1/widget/conversations/set_custom_attributes${window.location.search}`,
{
custom_attributes: customAttributes,
}
);
};

const deleteCustomAttribute = async customAttribute => {
return API.post(
`/api/v1/widget/conversations/destroy_custom_attributes${window.location.search}`,
{
custom_attribute: [customAttribute],
}
);
};

export {
createConversationAPI,
sendMessageAPI,
Expand All @@ -60,4 +78,6 @@ export {
setUserLastSeenAt,
sendEmailTranscript,
toggleStatus,
setCustomAttributes,
deleteCustomAttribute,
};
18 changes: 18 additions & 0 deletions app/javascript/widget/store/modules/conversation/actions.js
Expand Up @@ -6,6 +6,8 @@ import {
toggleTyping,
setUserLastSeenAt,
toggleStatus,
setCustomAttributes,
deleteCustomAttribute,
} from 'widget/api/conversation';

import { createTemporaryMessage, getNonDeletedMessages } from './helpers';
Expand Down Expand Up @@ -139,4 +141,20 @@ export const actions = {
resolveConversation: async () => {
await toggleStatus();
},

setCustomAttributes: async (_, customAttributes = {}) => {
try {
await setCustomAttributes(customAttributes);
} catch (error) {
// IgnoreError
}
},

deleteCustomAttribute: async (_, customAttribute) => {
try {
await deleteCustomAttribute(customAttribute);
} catch (error) {
// IgnoreError
}
},
};
2 changes: 2 additions & 0 deletions config/routes.rb
Expand Up @@ -226,6 +226,8 @@
resources :messages, only: [:index, :create, :update]
resources :conversations, only: [:index, :create] do
collection do
post :destroy_custom_attributes
post :set_custom_attributes
post :update_last_seen
post :toggle_typing
post :transcript
Expand Down
55 changes: 55 additions & 0 deletions spec/controllers/api/v1/widget/conversations_controller_spec.rb
Expand Up @@ -255,4 +255,59 @@
end
end
end

describe 'POST /api/v1/widget/conversations/set_custom_attributes' do
let(:params) { { website_token: web_widget.website_token, custom_attributes: { 'product_name': 'Chatwoot' } } }

context 'with invalid website token' do
it 'returns unauthorized' do
post '/api/v1/widget/conversations/set_custom_attributes', params: { website_token: '' }
expect(response).to have_http_status(:not_found)
end
end

context 'with correct website token' do
it 'sets the values when provided' do
post '/api/v1/widget/conversations/set_custom_attributes',
headers: { 'X-Auth-Token' => token },
params: params,
as: :json

expect(response).to have_http_status(:success)
conversation.reload
# conversation custom attributes should have "product_name" key with value "Chatwoot"
expect(conversation.custom_attributes).to include('product_name' => 'Chatwoot')
end
end
end

describe 'POST /api/v1/widget/conversations/destroy_custom_attributes' do
let(:params) { { website_token: web_widget.website_token, custom_attribute: ['product_name'] } }

context 'with invalid website token' do
it 'returns unauthorized' do
post '/api/v1/widget/conversations/destroy_custom_attributes', params: { website_token: '' }
expect(response).to have_http_status(:not_found)
end
end

context 'with correct website token' do
it 'sets the values when provided' do
# ensure conversation has the attribute
conversation.custom_attributes = { 'product_name': 'Chatwoot' }
conversation.save!
expect(conversation.custom_attributes).to include('product_name' => 'Chatwoot')

post '/api/v1/widget/conversations/destroy_custom_attributes',
headers: { 'X-Auth-Token' => token },
params: params,
as: :json

expect(response).to have_http_status(:success)
conversation.reload
# conversation custom attributes should not have "product_name" key with value "Chatwoot"
expect(conversation.custom_attributes).not_to include('product_name' => 'Chatwoot')
end
end
end
end

0 comments on commit a040aee

Please sign in to comment.