Merge branch 'develop' into feat/new-auth-screens
This commit is contained in:
commit
100f0b07ce
19 changed files with 284 additions and 96 deletions
|
@ -31,7 +31,7 @@ class ContactInboxBuilder
|
|||
return unless @contact.phone_number
|
||||
|
||||
# whatsapp doesn't want the + in e164 format
|
||||
"#{@contact.phone_number}.delete('+')"
|
||||
@contact.phone_number.delete('+').to_s
|
||||
end
|
||||
|
||||
def twilio_source_id
|
||||
|
|
|
@ -51,6 +51,6 @@ class Platform::Api::V1::UsersController < PlatformController
|
|||
end
|
||||
|
||||
def user_params
|
||||
params.permit(:name, :email, :password, custom_attributes: {})
|
||||
params.permit(:name, :display_name, :email, :password, custom_attributes: {})
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,7 +28,7 @@ import {
|
|||
suggestionsPlugin,
|
||||
triggerCharacters,
|
||||
} from '@chatwoot/prosemirror-schema/src/mentions/plugin';
|
||||
import { EditorState } from 'prosemirror-state';
|
||||
import { EditorState, Selection } from 'prosemirror-state';
|
||||
import { defaultMarkdownParser } from 'prosemirror-markdown';
|
||||
import { wootWriterSetup } from '@chatwoot/prosemirror-schema';
|
||||
|
||||
|
@ -61,23 +61,28 @@ export default {
|
|||
mixins: [eventListenerMixins],
|
||||
props: {
|
||||
value: { type: String, default: '' },
|
||||
editorId: { type: String, default: '' },
|
||||
placeholder: { type: String, default: '' },
|
||||
isPrivate: { type: Boolean, default: false },
|
||||
isFormatMode: { type: Boolean, default: false },
|
||||
enableSuggestions: { type: Boolean, default: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
lastValue: null,
|
||||
showUserMentions: false,
|
||||
showCannedMenu: false,
|
||||
mentionSearchKey: '',
|
||||
cannedSearchTerm: '',
|
||||
editorView: null,
|
||||
range: null,
|
||||
state: undefined,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
contentFromEditor() {
|
||||
return addMentionsToMarkdownSerializer(
|
||||
defaultMarkdownSerializer
|
||||
).serialize(this.editorView.state.doc);
|
||||
},
|
||||
plugins() {
|
||||
if (!this.enableSuggestions) {
|
||||
return [];
|
||||
|
@ -102,7 +107,6 @@ export default {
|
|||
onExit: () => {
|
||||
this.mentionSearchKey = '';
|
||||
this.showUserMentions = false;
|
||||
this.editorView = null;
|
||||
return false;
|
||||
},
|
||||
onKeyDown: ({ event }) => {
|
||||
|
@ -131,7 +135,6 @@ export default {
|
|||
onExit: () => {
|
||||
this.cannedSearchTerm = '';
|
||||
this.showCannedMenu = false;
|
||||
this.editorView = null;
|
||||
return false;
|
||||
},
|
||||
onKeyDown: ({ event }) => {
|
||||
|
@ -149,28 +152,33 @@ export default {
|
|||
this.$emit('toggle-canned-menu', !this.isPrivate && updatedValue);
|
||||
},
|
||||
value(newValue = '') {
|
||||
if (newValue !== this.lastValue) {
|
||||
const { tr } = this.state;
|
||||
if (this.isFormatMode) {
|
||||
this.state = createState(
|
||||
newValue,
|
||||
this.placeholder,
|
||||
this.plugins,
|
||||
this.isFormatMode
|
||||
);
|
||||
} else {
|
||||
tr.insertText(newValue, 0, tr.doc.content.size);
|
||||
this.state = this.view.state.apply(tr);
|
||||
}
|
||||
this.view.updateState(this.state);
|
||||
if (newValue !== this.contentFromEditor) {
|
||||
this.reloadState();
|
||||
}
|
||||
},
|
||||
editorId() {
|
||||
this.reloadState();
|
||||
},
|
||||
isPrivate() {
|
||||
this.reloadState();
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.state = createState(this.value, this.placeholder, this.plugins);
|
||||
},
|
||||
mounted() {
|
||||
this.view = new EditorView(this.$refs.editor, {
|
||||
this.createEditorView();
|
||||
this.editorView.updateState(this.state);
|
||||
this.focusEditorInputField();
|
||||
},
|
||||
methods: {
|
||||
reloadState() {
|
||||
this.state = createState(this.value, this.placeholder, this.plugins);
|
||||
this.editorView.updateState(this.state);
|
||||
this.focusEditorInputField();
|
||||
},
|
||||
createEditorView() {
|
||||
this.editorView = new EditorView(this.$refs.editor, {
|
||||
state: this.state,
|
||||
dispatchTransaction: tx => {
|
||||
this.state = this.state.apply(tx);
|
||||
|
@ -194,9 +202,7 @@ export default {
|
|||
},
|
||||
},
|
||||
});
|
||||
this.focusEditorInputField();
|
||||
},
|
||||
methods: {
|
||||
handleKeyEvents(e) {
|
||||
if (hasPressedAltAndPKey(e)) {
|
||||
this.focusEditorInputField();
|
||||
|
@ -206,47 +212,59 @@ export default {
|
|||
}
|
||||
},
|
||||
focusEditorInputField() {
|
||||
this.$refs.editor.querySelector('div.ProseMirror-woot-style').focus();
|
||||
const { tr } = this.editorView.state;
|
||||
const selection = Selection.atEnd(tr.doc);
|
||||
|
||||
this.editorView.dispatch(tr.setSelection(selection));
|
||||
this.editorView.focus();
|
||||
},
|
||||
insertMentionNode(mentionItem) {
|
||||
if (!this.view) {
|
||||
if (!this.editorView) {
|
||||
return null;
|
||||
}
|
||||
const node = this.view.state.schema.nodes.mention.create({
|
||||
const node = this.editorView.state.schema.nodes.mention.create({
|
||||
userId: mentionItem.key,
|
||||
userFullName: mentionItem.label,
|
||||
});
|
||||
|
||||
const tr = this.view.state.tr.replaceWith(
|
||||
const tr = this.editorView.state.tr.replaceWith(
|
||||
this.range.from,
|
||||
this.range.to,
|
||||
node
|
||||
);
|
||||
this.state = this.view.state.apply(tr);
|
||||
this.state = this.editorView.state.apply(tr);
|
||||
return this.emitOnChange();
|
||||
},
|
||||
|
||||
insertCannedResponse(cannedItem) {
|
||||
if (!this.view) {
|
||||
if (!this.editorView) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tr = this.view.state.tr.insertText(
|
||||
const tr = this.editorView.state.tr.insertText(
|
||||
cannedItem,
|
||||
this.range.from,
|
||||
this.range.to
|
||||
);
|
||||
this.state = this.view.state.apply(tr);
|
||||
return this.emitOnChange();
|
||||
this.state = this.editorView.state.apply(tr);
|
||||
this.emitOnChange();
|
||||
|
||||
// Hacky fix for #5501
|
||||
this.state = createState(
|
||||
this.contentFromEditor,
|
||||
this.placeholder,
|
||||
this.plugins
|
||||
);
|
||||
this.editorView.updateState(this.state);
|
||||
return false;
|
||||
},
|
||||
|
||||
emitOnChange() {
|
||||
this.view.updateState(this.state);
|
||||
this.lastValue = addMentionsToMarkdownSerializer(
|
||||
defaultMarkdownSerializer
|
||||
).serialize(this.state.doc);
|
||||
this.$emit('input', this.lastValue);
|
||||
this.editorView.updateState(this.state);
|
||||
|
||||
this.$emit('input', this.contentFromEditor);
|
||||
},
|
||||
|
||||
hideMentions() {
|
||||
this.showUserMentions = false;
|
||||
},
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
<woot-message-editor
|
||||
v-else
|
||||
v-model="message"
|
||||
:editor-id="editorStateId"
|
||||
class="input"
|
||||
:is-private="isOnPrivateNote"
|
||||
:placeholder="messagePlaceHolder"
|
||||
|
@ -429,6 +430,13 @@ export default {
|
|||
profilePath() {
|
||||
return frontendURL(`accounts/${this.accountId}/profile/settings`);
|
||||
},
|
||||
conversationId() {
|
||||
return this.currentChat.id;
|
||||
},
|
||||
editorStateId() {
|
||||
const key = `draft-${this.conversationIdByRoute}-${this.replyType}`;
|
||||
return key;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
currentChat(conversation) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
export const LOCAL_STORAGE_KEYS = {
|
||||
DISMISSED_UPDATES: 'dismissedUpdates',
|
||||
WIDGET_BUILDER: 'widgetBubble_',
|
||||
DRAFT_MESSAGES: 'draftMessages',
|
||||
};
|
||||
|
||||
export const LocalStorage = {
|
||||
|
|
|
@ -46,6 +46,7 @@ export default {
|
|||
return {
|
||||
articleTitle: '',
|
||||
articleContent: '',
|
||||
saveArticle: () => {},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#
|
||||
# index_articles_on_associated_article_id (associated_article_id)
|
||||
# index_articles_on_author_id (author_id)
|
||||
# index_articles_on_slug (slug)
|
||||
# index_articles_on_slug (slug) UNIQUE
|
||||
#
|
||||
class Article < ApplicationRecord
|
||||
include PgSearch::Model
|
||||
|
@ -45,6 +45,8 @@ class Article < ApplicationRecord
|
|||
belongs_to :author, class_name: 'User'
|
||||
|
||||
before_validation :ensure_account_id
|
||||
before_validation :ensure_article_slug
|
||||
|
||||
validates :account_id, presence: true
|
||||
validates :category_id, presence: true
|
||||
validates :author_id, presence: true
|
||||
|
@ -112,4 +114,8 @@ class Article < ApplicationRecord
|
|||
def ensure_account_id
|
||||
self.account_id = portal&.account_id
|
||||
end
|
||||
|
||||
def ensure_article_slug
|
||||
self.slug ||= "#{Time.now.utc.to_i}-#{title.underscore.parameterize(separator: '-')}" if title.present?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
class ContactInbox < ApplicationRecord
|
||||
include Pubsubable
|
||||
include RegexHelper
|
||||
validates :inbox_id, presence: true
|
||||
validates :contact_id, presence: true
|
||||
validates :source_id, presence: true
|
||||
|
@ -51,10 +52,10 @@ class ContactInbox < ApplicationRecord
|
|||
|
||||
def validate_twilio_source_id
|
||||
# https://www.twilio.com/docs/glossary/what-e164#regex-matching-for-e164
|
||||
if inbox.channel.medium == 'sms' && !/\+[1-9]\d{1,14}\z/.match?(source_id)
|
||||
errors.add(:source_id, 'invalid source id for twilio sms inbox. valid Regex /\+[1-9]\d{1,14}\z/')
|
||||
elsif inbox.channel.medium == 'whatsapp' && !/whatsapp:\+[1-9]\d{1,14}\z/.match?(source_id)
|
||||
errors.add(:source_id, 'invalid source id for twilio whatsapp inbox. valid Regex /whatsapp:\+[1-9]\d{1,14}\z/')
|
||||
if inbox.channel.medium == 'sms' && !TWILIO_CHANNEL_SMS_REGEX.match?(source_id)
|
||||
errors.add(:source_id, "invalid source id for twilio sms inbox. valid Regex #{TWILIO_CHANNEL_SMS_REGEX}")
|
||||
elsif inbox.channel.medium == 'whatsapp' && !TWILIO_CHANNEL_WHATSAPP_REGEX.match?(source_id)
|
||||
errors.add(:source_id, "invalid source id for twilio whatsapp inbox. valid Regex #{TWILIO_CHANNEL_WHATSAPP_REGEX}")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -62,8 +63,15 @@ class ContactInbox < ApplicationRecord
|
|||
errors.add(:source_id, "invalid source id for Email inbox. valid Regex #{Devise.email_regexp}") unless Devise.email_regexp.match?(source_id)
|
||||
end
|
||||
|
||||
def validate_whatsapp_source_id
|
||||
return if WHATSAPP_CHANNEL_REGEX.match?(source_id)
|
||||
|
||||
errors.add(:source_id, "invalid source id for whatsapp inbox. valid Regex #{WHATSAPP_CHANNEL_REGEX}")
|
||||
end
|
||||
|
||||
def valid_source_id_format?
|
||||
validate_twilio_source_id if inbox.channel_type == 'Channel::TwilioSms'
|
||||
validate_email_source_id if inbox.channel_type == 'Channel::Email'
|
||||
validate_whatsapp_source_id if inbox.channel_type == 'Channel::Whatsapp'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -241,7 +241,7 @@ class Message < ApplicationRecord
|
|||
end
|
||||
|
||||
def validate_attachments_limit(_attachment)
|
||||
errors.add(attachments: 'exceeded maximum allowed') if attachments.size >= NUMBER_OF_PERMITTED_ATTACHMENTS
|
||||
errors.add(:attachments, message: 'exceeded maximum allowed') if attachments.size >= NUMBER_OF_PERMITTED_ATTACHMENTS
|
||||
end
|
||||
|
||||
def set_conversation_activity
|
||||
|
|
6
db/migrate/20220930025317_add_unique_index_to_slug.rb
Normal file
6
db/migrate/20220930025317_add_unique_index_to_slug.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
class AddUniqueIndexToSlug < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
remove_index :articles, :slug
|
||||
add_index :articles, :slug, unique: true
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2022_09_26_164441) do
|
||||
ActiveRecord::Schema.define(version: 2022_09_30_025317) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_stat_statements"
|
||||
|
@ -134,7 +134,7 @@ ActiveRecord::Schema.define(version: 2022_09_26_164441) do
|
|||
t.string "slug", null: false
|
||||
t.index ["associated_article_id"], name: "index_articles_on_associated_article_id"
|
||||
t.index ["author_id"], name: "index_articles_on_author_id"
|
||||
t.index ["slug"], name: "index_articles_on_slug"
|
||||
t.index ["slug"], name: "index_articles_on_slug", unique: true
|
||||
end
|
||||
|
||||
create_table "attachments", id: :serial, force: :cascade do |t|
|
||||
|
|
|
@ -6,4 +6,8 @@ module RegexHelper
|
|||
# shouldn't start with a underscore or hyphen
|
||||
UNICODE_CHARACTER_NUMBER_HYPHEN_UNDERSCORE = Regexp.new('\A[\p{L}\p{N}]+[\p{L}\p{N}_-]+\Z')
|
||||
MENTION_REGEX = Regexp.new('\[(@[\w_. ]+)\]\(mention://(?:user|team)/\d+/(.*?)+\)')
|
||||
|
||||
TWILIO_CHANNEL_SMS_REGEX = Regexp.new('^\+\d{1,14}\z')
|
||||
TWILIO_CHANNEL_WHATSAPP_REGEX = Regexp.new('^whatsapp:\+\d{1,14}\z')
|
||||
WHATSAPP_CHANNEL_REGEX = Regexp.new('^\d{1,14}\z')
|
||||
end
|
||||
|
|
|
@ -17,7 +17,7 @@ describe ::ContactInboxBuilder do
|
|||
source_id: contact.phone_number
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).to be(existing_contact_inbox.id)
|
||||
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
||||
end
|
||||
|
||||
it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do
|
||||
|
@ -27,7 +27,7 @@ describe ::ContactInboxBuilder do
|
|||
inbox_id: twilio_inbox.id
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).to be(existing_contact_inbox.id)
|
||||
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
||||
end
|
||||
|
||||
it 'creates a new contact inbox when different source id is provided' do
|
||||
|
@ -38,8 +38,8 @@ describe ::ContactInboxBuilder do
|
|||
source_id: '+224213223422'
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).not_to be(existing_contact_inbox.id)
|
||||
expect(contact_inbox.source_id).not_to be('+224213223422')
|
||||
expect(contact_inbox.id).not_to eq(existing_contact_inbox.id)
|
||||
expect(contact_inbox.source_id).to eq('+224213223422')
|
||||
end
|
||||
|
||||
it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do
|
||||
|
@ -48,7 +48,7 @@ describe ::ContactInboxBuilder do
|
|||
inbox_id: twilio_inbox.id
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.source_id).not_to be(contact.phone_number)
|
||||
expect(contact_inbox.source_id).to eq(contact.phone_number)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -64,7 +64,7 @@ describe ::ContactInboxBuilder do
|
|||
source_id: "whatsapp:#{contact.phone_number}"
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).to be(existing_contact_inbox.id)
|
||||
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
||||
end
|
||||
|
||||
it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do
|
||||
|
@ -74,7 +74,7 @@ describe ::ContactInboxBuilder do
|
|||
inbox_id: twilio_inbox.id
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).to be(existing_contact_inbox.id)
|
||||
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
||||
end
|
||||
|
||||
it 'creates a new contact inbox when different source id is provided' do
|
||||
|
@ -85,8 +85,8 @@ describe ::ContactInboxBuilder do
|
|||
source_id: 'whatsapp:+555555'
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).not_to be(existing_contact_inbox.id)
|
||||
expect(contact_inbox.source_id).not_to be('whatsapp:+55555')
|
||||
expect(contact_inbox.id).not_to eq(existing_contact_inbox.id)
|
||||
expect(contact_inbox.source_id).to eq('whatsapp:+555555')
|
||||
end
|
||||
|
||||
it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do
|
||||
|
@ -95,7 +95,53 @@ describe ::ContactInboxBuilder do
|
|||
inbox_id: twilio_inbox.id
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.source_id).not_to be("whatsapp:#{contact.phone_number}")
|
||||
expect(contact_inbox.source_id).to eq("whatsapp:#{contact.phone_number}")
|
||||
end
|
||||
end
|
||||
|
||||
describe 'whatsapp inbox' do
|
||||
let(:whatsapp_inbox) { create(:channel_whatsapp, account: account, sync_templates: false, validate_provider_config: false).inbox }
|
||||
|
||||
it 'does not create contact inbox when contact inbox already exists with the source id provided' do
|
||||
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: contact.phone_number&.delete('+'))
|
||||
contact_inbox = described_class.new(
|
||||
contact_id: contact.id,
|
||||
inbox_id: whatsapp_inbox.id,
|
||||
source_id: contact.phone_number&.delete('+')
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).to be(existing_contact_inbox.id)
|
||||
end
|
||||
|
||||
it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do
|
||||
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: contact.phone_number&.delete('+'))
|
||||
contact_inbox = described_class.new(
|
||||
contact_id: contact.id,
|
||||
inbox_id: whatsapp_inbox.id
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).to be(existing_contact_inbox.id)
|
||||
end
|
||||
|
||||
it 'creates a new contact inbox when different source id is provided' do
|
||||
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: contact.phone_number&.delete('+'))
|
||||
contact_inbox = described_class.new(
|
||||
contact_id: contact.id,
|
||||
inbox_id: whatsapp_inbox.id,
|
||||
source_id: '555555'
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).not_to be(existing_contact_inbox.id)
|
||||
expect(contact_inbox.source_id).not_to be('555555')
|
||||
end
|
||||
|
||||
it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do
|
||||
contact_inbox = described_class.new(
|
||||
contact_id: contact.id,
|
||||
inbox_id: whatsapp_inbox.id
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.source_id).to eq(contact.phone_number&.delete('+'))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -111,7 +157,7 @@ describe ::ContactInboxBuilder do
|
|||
source_id: contact.phone_number
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).to be(existing_contact_inbox.id)
|
||||
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
||||
end
|
||||
|
||||
it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do
|
||||
|
@ -121,7 +167,7 @@ describe ::ContactInboxBuilder do
|
|||
inbox_id: sms_inbox.id
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).to be(existing_contact_inbox.id)
|
||||
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
||||
end
|
||||
|
||||
it 'creates a new contact inbox when different source id is provided' do
|
||||
|
@ -132,8 +178,8 @@ describe ::ContactInboxBuilder do
|
|||
source_id: '+224213223422'
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).not_to be(existing_contact_inbox.id)
|
||||
expect(contact_inbox.source_id).not_to be('+224213223422')
|
||||
expect(contact_inbox.id).not_to eq(existing_contact_inbox.id)
|
||||
expect(contact_inbox.source_id).to eq('+224213223422')
|
||||
end
|
||||
|
||||
it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do
|
||||
|
@ -142,7 +188,7 @@ describe ::ContactInboxBuilder do
|
|||
inbox_id: sms_inbox.id
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.source_id).not_to be(contact.phone_number)
|
||||
expect(contact_inbox.source_id).to eq(contact.phone_number)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -158,7 +204,7 @@ describe ::ContactInboxBuilder do
|
|||
source_id: contact.email
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).to be(existing_contact_inbox.id)
|
||||
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
||||
end
|
||||
|
||||
it 'does not create contact inbox when contact inbox already exists with email and source id is not provided' do
|
||||
|
@ -168,7 +214,7 @@ describe ::ContactInboxBuilder do
|
|||
inbox_id: email_inbox.id
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).to be(existing_contact_inbox.id)
|
||||
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
||||
end
|
||||
|
||||
it 'creates a new contact inbox when different source id is provided' do
|
||||
|
@ -179,8 +225,8 @@ describe ::ContactInboxBuilder do
|
|||
source_id: 'xyc@xyc.com'
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).not_to be(existing_contact_inbox.id)
|
||||
expect(contact_inbox.source_id).not_to be('xyc@xyc.com')
|
||||
expect(contact_inbox.id).not_to eq(existing_contact_inbox.id)
|
||||
expect(contact_inbox.source_id).to eq('xyc@xyc.com')
|
||||
end
|
||||
|
||||
it 'creates a contact inbox with contact email when source id not provided and no contact inbox exists' do
|
||||
|
@ -189,7 +235,7 @@ describe ::ContactInboxBuilder do
|
|||
inbox_id: email_inbox.id
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.source_id).not_to be(contact.email)
|
||||
expect(contact_inbox.source_id).to eq(contact.email)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -205,7 +251,7 @@ describe ::ContactInboxBuilder do
|
|||
source_id: 'test'
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).to be(existing_contact_inbox.id)
|
||||
expect(contact_inbox.id).to eq(existing_contact_inbox.id)
|
||||
end
|
||||
|
||||
it 'creates a new contact inbox when different source id is provided' do
|
||||
|
@ -216,8 +262,8 @@ describe ::ContactInboxBuilder do
|
|||
source_id: 'test'
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).not_to be(existing_contact_inbox.id)
|
||||
expect(contact_inbox.source_id).not_to be('test')
|
||||
expect(contact_inbox.id).not_to eq(existing_contact_inbox.id)
|
||||
expect(contact_inbox.source_id).to eq('test')
|
||||
end
|
||||
|
||||
it 'creates a contact inbox with SecureRandom.uuid when source id not provided and no contact inbox exists' do
|
||||
|
|
|
@ -96,15 +96,24 @@ RSpec.describe 'Platform Users API', type: :request do
|
|||
|
||||
it 'creates a new user and permissible for the user' do
|
||||
expect do
|
||||
post '/platform/api/v1/users/', params: { name: 'test', email: 'test@test.com', password: 'Password1!',
|
||||
post '/platform/api/v1/users/', params: { name: 'test', display_name: 'displaytest',
|
||||
email: 'test@test.com', password: 'Password1!',
|
||||
custom_attributes: { test: 'test_create' } },
|
||||
headers: { api_access_token: platform_app.access_token.token }, as: :json
|
||||
end.not_to enqueue_mail
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
data = JSON.parse(response.body)
|
||||
expect(data['email']).to eq('test@test.com')
|
||||
expect(data['custom_attributes']['test']).to eq('test_create')
|
||||
expect(data).to match(
|
||||
hash_including(
|
||||
'name' => 'test',
|
||||
'display_name' => 'displaytest',
|
||||
'email' => 'test@test.com',
|
||||
'custom_attributes' => {
|
||||
'test' => 'test_create'
|
||||
}
|
||||
)
|
||||
)
|
||||
expect(platform_app.platform_app_permissibles.first.permissible_id).to eq data['id']
|
||||
end
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ FactoryBot.define do
|
|||
account_id { 1 }
|
||||
category_id { 1 }
|
||||
author_id { 1 }
|
||||
title { 'MyString' }
|
||||
slug { 'MyString' }
|
||||
title { Faker::Movie.title }
|
||||
content { 'MyText' }
|
||||
description { 'MyDescrption' }
|
||||
status { 1 }
|
||||
|
|
|
@ -15,6 +15,8 @@ def generate_source_id(contact_inbox)
|
|||
contact_inbox.inbox.channel.medium == 'sms' ? Faker::PhoneNumber.cell_phone_in_e164 : "whatsapp:#{Faker::PhoneNumber.cell_phone_in_e164}"
|
||||
when 'Channel::Email'
|
||||
"#{SecureRandom.uuid}@acme.inc"
|
||||
when 'Channel::Whatsapp'
|
||||
Faker::PhoneNumber.cell_phone_in_e164.delete('+')
|
||||
else
|
||||
SecureRandom.uuid
|
||||
end
|
||||
|
|
|
@ -119,11 +119,23 @@ RSpec.describe Article, type: :model do
|
|||
records = portal_1.articles.search(params)
|
||||
expect(records.count).to eq(2)
|
||||
end
|
||||
|
||||
it 'auto saves article slug' do
|
||||
article = create(:article, category_id: category_1.id, title: 'the awesome article 1', content: 'This is the content', portal_id: portal_1.id,
|
||||
author_id: user.id)
|
||||
expect(article.slug).to include('the-awesome-article-1')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with pagination' do
|
||||
it 'returns paginated articles' do
|
||||
create_list(:article, 30, category_id: category_2.id, slug: 'title-1', title: 'title 1', portal_id: portal_2.id, author_id: user.id)
|
||||
build_list(:article, 30) do |record, i|
|
||||
record.category_id = category_2.id
|
||||
record.title = "title #{i}"
|
||||
record.portal_id = portal_2.id
|
||||
record.author_id = user.id
|
||||
record.save!
|
||||
end
|
||||
params = { category_slug: 'category_2' }
|
||||
records = portal_2.articles.search(params)
|
||||
expect(records.count).to eq(25)
|
||||
|
|
|
@ -37,4 +37,59 @@ RSpec.describe ContactInbox do
|
|||
expect(obj.pubsub_token).to eq(new_token)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
context 'when source_id' do
|
||||
it 'validates whatsapp channel source_id' do
|
||||
whatsapp_inbox = create(:channel_whatsapp, sync_templates: false, validate_provider_config: false).inbox
|
||||
contact = create(:contact)
|
||||
valid_source_id = build(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: '1234567890')
|
||||
ci_character_in_source_id = build(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: '1234567890aaa')
|
||||
ci_plus_in_source_id = build(:contact_inbox, contact: contact, inbox: whatsapp_inbox, source_id: '+1234567890')
|
||||
expect(valid_source_id.valid?).to be(true)
|
||||
expect(ci_character_in_source_id.valid?).to be(false)
|
||||
expect(ci_character_in_source_id.errors.full_messages).to eq(
|
||||
['Source invalid source id for whatsapp inbox. valid Regex (?-mix:^\\d{1,14}\\z)']
|
||||
)
|
||||
expect(ci_plus_in_source_id.valid?).to be(false)
|
||||
expect(ci_plus_in_source_id.errors.full_messages).to eq(
|
||||
['Source invalid source id for whatsapp inbox. valid Regex (?-mix:^\\d{1,14}\\z)']
|
||||
)
|
||||
end
|
||||
|
||||
it 'validates twilio sms channel source_id' do
|
||||
twilio_sms_inbox = create(:channel_twilio_sms).inbox
|
||||
contact = create(:contact)
|
||||
valid_source_id = build(:contact_inbox, contact: contact, inbox: twilio_sms_inbox, source_id: '+1234567890')
|
||||
ci_character_in_source_id = build(:contact_inbox, contact: contact, inbox: twilio_sms_inbox, source_id: '+1234567890aaa')
|
||||
ci_without_plus_in_source_id = build(:contact_inbox, contact: contact, inbox: twilio_sms_inbox, source_id: '1234567890')
|
||||
expect(valid_source_id.valid?).to be(true)
|
||||
expect(ci_character_in_source_id.valid?).to be(false)
|
||||
expect(ci_character_in_source_id.errors.full_messages).to eq(
|
||||
['Source invalid source id for twilio sms inbox. valid Regex (?-mix:^\\+\\d{1,14}\\z)']
|
||||
)
|
||||
expect(ci_without_plus_in_source_id.valid?).to be(false)
|
||||
expect(ci_without_plus_in_source_id.errors.full_messages).to eq(
|
||||
['Source invalid source id for twilio sms inbox. valid Regex (?-mix:^\\+\\d{1,14}\\z)']
|
||||
)
|
||||
end
|
||||
|
||||
it 'validates twilio whatsapp channel source_id' do
|
||||
twilio_whatsapp_inbox = create(:channel_twilio_sms, medium: :whatsapp).inbox
|
||||
contact = create(:contact)
|
||||
valid_source_id = build(:contact_inbox, contact: contact, inbox: twilio_whatsapp_inbox, source_id: 'whatsapp:+1234567890')
|
||||
ci_character_in_source_id = build(:contact_inbox, contact: contact, inbox: twilio_whatsapp_inbox, source_id: 'whatsapp:+1234567890aaa')
|
||||
ci_without_plus_in_source_id = build(:contact_inbox, contact: contact, inbox: twilio_whatsapp_inbox, source_id: 'whatsapp:1234567890')
|
||||
expect(valid_source_id.valid?).to be(true)
|
||||
expect(ci_character_in_source_id.valid?).to be(false)
|
||||
expect(ci_character_in_source_id.errors.full_messages).to eq(
|
||||
['Source invalid source id for twilio whatsapp inbox. valid Regex (?-mix:^whatsapp:\\+\\d{1,14}\\z)']
|
||||
)
|
||||
expect(ci_without_plus_in_source_id.valid?).to be(false)
|
||||
expect(ci_without_plus_in_source_id.errors.full_messages).to eq(
|
||||
['Source invalid source id for twilio whatsapp inbox. valid Regex (?-mix:^whatsapp:\\+\\d{1,14}\\z)']
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -106,6 +106,19 @@ RSpec.describe Message, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when attachments size maximum' do
|
||||
let(:message) { build(:message, content_type: nil, account: create(:account)) }
|
||||
|
||||
it 'add errors to message for attachment size is more than allowed limit' do
|
||||
16.times.each do
|
||||
attachment = message.attachments.new(account_id: message.account_id, file_type: :image)
|
||||
attachment.file.attach(io: File.open(Rails.root.join('spec/assets/avatar.png')), filename: 'avatar.png', content_type: 'image/png')
|
||||
end
|
||||
|
||||
expect(message.errors.messages).to eq({ attachments: ['exceeded maximum allowed'] })
|
||||
end
|
||||
end
|
||||
|
||||
context 'when email notifiable message' do
|
||||
let(:message) { build(:message, content_type: nil, account: create(:account)) }
|
||||
|
||||
|
|
Loading…
Reference in a new issue